From 51aebc35a66d6711ea50e71e1f3bf12a5cdf4416 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Tue, 25 Jul 2023 18:20:09 +0000 Subject: [PATCH 01/56] Set log level in environment --- axis-ptz-controller.env | 2 +- axis-ptz-controller/axis_ptz_controller.py | 2 +- axis-ptz-controller/axis_ptz_utilities.py | 2 +- axis-ptz-controller/camera_configuration.py | 3 ++- axis-ptz-controller/camera_control.py | 3 ++- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/axis-ptz-controller.env b/axis-ptz-controller.env index 933b8e0..8615309 100644 --- a/axis-ptz-controller.env +++ b/axis-ptz-controller.env @@ -31,4 +31,4 @@ USE_CAMERA=False INCLUDE_AGE=True LOG_TO_MQTT=False CONTINUE_ON_EXCEPTION=False - +LOG_LEVEL="DEBUG" diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index b45afa6..579a6f4 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -37,7 +37,7 @@ "warning": {"color": "yellow"}, } coloredlogs.install( - level=logging.INFO, + level=os.environ.get("LOG_LEVEL", "INFO"), fmt="%(asctime)s.%(msecs)03d \033[0;90m%(levelname)-8s " "" "\033[0;36m%(filename)-18s%(lineno)3d\033[00m " diff --git a/axis-ptz-controller/axis_ptz_utilities.py b/axis-ptz-controller/axis_ptz_utilities.py index d1e3caa..925f256 100644 --- a/axis-ptz-controller/axis_ptz_utilities.py +++ b/axis-ptz-controller/axis_ptz_utilities.py @@ -26,7 +26,7 @@ "warning": {"color": "yellow"}, } coloredlogs.install( - level=logging.INFO, + level=os.environ.get("LOG_LEVEL", "INFO"), fmt="%(asctime)s.%(msecs)03d \033[0;90m%(levelname)-8s " "" "\033[0;36m%(filename)-18s%(lineno)3d\033[00m " diff --git a/axis-ptz-controller/camera_configuration.py b/axis-ptz-controller/camera_configuration.py index 50654bc..ee0e5fe 100644 --- a/axis-ptz-controller/camera_configuration.py +++ b/axis-ptz-controller/camera_configuration.py @@ -2,6 +2,7 @@ import coloredlogs import datetime import logging +import os import requests from requests.auth import HTTPDigestAuth @@ -19,7 +20,7 @@ "warning": {"color": "yellow"}, } coloredlogs.install( - level=logging.INFO, + level=os.environ.get("LOG_LEVEL", "INFO"), fmt="%(asctime)s.%(msecs)03d \033[0;90m%(levelname)-8s " "" "\033[0;36m%(filename)-18s%(lineno)3d\033[00m " diff --git a/axis-ptz-controller/camera_control.py b/axis-ptz-controller/camera_control.py index 9766687..94e349a 100644 --- a/axis-ptz-controller/camera_control.py +++ b/axis-ptz-controller/camera_control.py @@ -1,5 +1,6 @@ import coloredlogs import logging +import os from sensecam_control import vapix_control from typing import Tuple, Union @@ -16,7 +17,7 @@ "warning": {"color": "yellow"}, } coloredlogs.install( - level=logging.INFO, + level=os.environ.get("LOG_LEVEL", "INFO"), fmt="%(asctime)s.%(msecs)03d \033[0;90m%(levelname)-8s " "" "\033[0;36m%(filename)-18s%(lineno)3d\033[00m " From 5ca2c32db3577901b716fc0ffc875a4d57e0333f Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Wed, 26 Jul 2023 13:45:39 +0000 Subject: [PATCH 02/56] Publish base64 encoded images --- axis-ptz-controller.env | 3 +- axis-ptz-controller/axis_ptz_controller.py | 85 ++++++++++++++++++++-- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/axis-ptz-controller.env b/axis-ptz-controller.env index ecfb882..a88ce89 100644 --- a/axis-ptz-controller.env +++ b/axis-ptz-controller.env @@ -6,7 +6,8 @@ CAMERA_PASSWORD=iqtpass1 CONFIG_TOPIC="skyscan/config/json" ORIENTATION_TOPIC="skyscan/calibration/json" OBJECT_TOPIC=/Multimodal/${HOSTNAME}/selection/edgetech-axis-ptz-controller/JSON -CAPTURE_TOPIC="skyscan/captures/json" +ENCODED_IMAGE_TOPIC="skyscan/encoded-image/json" +IMAGE_METADATA_TOPIC="skyscan/image-metadata/json" LOGGER_TOPIC="skyscan/logger/json" HEARTBEAT_INTERVAL=10 TRIPOD_LATITUDE=38.89294 diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index af52ecc..ff02dda 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -1,4 +1,5 @@ import ast +import base64 import coloredlogs from datetime import datetime import json @@ -57,7 +58,8 @@ def __init__( config_topic: str, orientation_topic: str, object_topic: str, - capture_topic: str, + encoded_image_topic: str, + image_metadata_topic: str, logger_topic: str, heartbeat_interval: int, lambda_t: float = 0.0, @@ -102,8 +104,10 @@ def __init__( MQTT topic for subscribing to orientation messages object_topic: str MQTT topic for subscribing to object messages - capture_topic: str - MQTT topic for publising capture messages + encoded_image_topic: str + MQTT topic for publising images in base64 encoding + image_metadata_topic: str + MQTT topic for publising image metadata logger_topic: str MQTT topic for publishing logger messages heartbeat_interval: int @@ -168,7 +172,8 @@ def __init__( self.config_topic = config_topic self.orientation_topic = orientation_topic self.object_topic = object_topic - self.capture_topic = capture_topic + self.encoded_image_topic = encoded_image_topic + self.image_metadata_topic = image_metadata_topic self.logger_topic = logger_topic self.heartbeat_interval = heartbeat_interval self.lambda_t = lambda_t @@ -347,7 +352,8 @@ def __init__( config_topic = {config_topic} orientation_topic = {orientation_topic} object_topic = {object_topic} - capture_topic = {capture_topic} + encoded_image_topic = {encoded_image_topic} + image_metadata_topic = {image_metadata_topic} logger_topic = {logger_topic} heartbeat_interval = {heartbeat_interval} lambda_t = {lambda_t} @@ -540,7 +546,6 @@ def _object_callback( Returns ------- None - """ # Assign identifier, time, position, and velocity of the # object @@ -768,6 +773,53 @@ def _object_callback( logging.debug(f"Publishing logger msg: {msg}") self.publish_to_topic(self.logger_topic, json.dumps(msg)) + def _publish_data(self: Any, data: Dict[str, str]) -> bool: + """Leverages edgetech-core functionality to publish a JSON + payload to the MQTT broker on the topic specified by type. + + Parameters + ---------- + data : Dict[str, str] + Dictionary that maps keys to type and payload + + Returns + ------- + success : bool + Returns True if successful publish, else False + """ + # Generate payload as JSON + payload = self.generate_payload_json( + push_timestamp=int(datetime.utcnow().timestamp()), + device_type=os.environ.get("DEVICE_TYPE", "Collector"), + id_=self.hostname, + deployment_id=os.environ.get( + "DEPLOYMENT_ID", f"Unknown-Location-{self.hostname}" + ), + current_location=os.envrion.get("CURRENT_LOCATION", "-90, -180"), + status="Debug", + message_type="Event", + model_version="null", + firmware_version="v0.0.0", + data_payload_type=data["type"], + data_payload=data["payload"], + ) + + # Publish payload to the topic selected by type + if data["type"] == "Encoded Image": + topic = self.encoded_image_topic + + elif data["type"] == "Image Metadata": + topic = self.image_metadata_topic + + success = self.publish_to_topic(topic, payload) + if success: + logging.info(f"Successfully published data on channel {topic}: {data}") + + else: + logging.error(f"Failed to publish data on channel {topic}: {data}") + + return success + def _compute_pan_rate_index(self, rho_dot: float) -> int: """Compute pan rate index between -100 and 100 using rates in deg/s, limiting the results to the specified minimum and @@ -831,6 +883,7 @@ def _capture_image(self) -> None: None """ if self.do_capture: + # Capture an image in JPEG format self.capture_time = time() datetime_c = datetime.utcnow() @@ -858,6 +911,16 @@ def _capture_image(self) -> None: logging.error(f"Could not capture image to directory: {d}: {e}") return + # Publish the image after base64 encoding + with open(image_filepath, "rb") as image_file: + encoded_image = base64.b64encode(image_file.read()) + self._publish_data( + { + "type": "Encoded Image", + "payload": encoded_image, + } + ) + # Populate and publish image metadata, getting current pan # and tilt, and accounting for object message age relative # to the image capture @@ -882,7 +945,12 @@ def _capture_image(self) -> None: logging.debug( f"Publishing metadata: {image_metadata}, for object: {self.object_id}, at: {self.capture_time}" ) - self.publish_to_topic(self.capture_topic, json.dumps(image_metadata)) + self._publish_data( + { + "type": "Image Metadata", + "payload": json.dumps(image_metadata), + } + ) def _update_pointing(self) -> None: """Update values of camera pan and tilt using current pan and @@ -987,7 +1055,8 @@ def make_controller() -> AxisPtzController: config_topic=os.getenv("CONFIG_TOPIC", ""), orientation_topic=os.getenv("ORIENTATION_TOPIC", ""), object_topic=os.getenv("OBJECT_TOPIC", ""), - capture_topic=os.getenv("CAPTURE_TOPIC", ""), + encoded_image_topic=os.getenv("ENCODED_IMAGE_TOPIC", ""), + image_metadata_topic=os.getenv("IMAGE_METADATA_TOPIC", ""), logger_topic=os.getenv("LOGGER_TOPIC", ""), heartbeat_interval=int(os.getenv("HEARTBEAT_INTERVAL", 10)), lambda_t=float(os.getenv("TRIPOD_LONGITUDE", 0.0)), From 4686aee406beade5a413b124885ca20ff08871bd Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Wed, 26 Jul 2023 18:55:24 +0000 Subject: [PATCH 03/56] Improve configuration handling. Still need to handle side effects. --- axis-ptz-controller/axis_ptz_controller.py | 119 ++++++++++++++------- 1 file changed, 79 insertions(+), 40 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 579a6f4..122b532 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -1,3 +1,8 @@ +"""Defines the AxisPtzController child class of BaseMQTTPubSub, and a +method for making AxisPtzController instances. Instatiates an +AxisPtzController, and executes its main() method when run as a +module. +""" import ast import coloredlogs from datetime import datetime @@ -344,44 +349,7 @@ def __init__( signal.signal(signal.SIGTERM, self._exit_handler) # Log configuration parameters - logging.info( - f"""AxisPtzController initialized with parameters: - - camera_ip = {camera_ip} - camera_user = {camera_user} - camera_password = {camera_password} - config_topic = {config_topic} - orientation_topic = {orientation_topic} - object_topic = {object_topic} - capture_topic = {capture_topic} - logger_topic = {logger_topic} - heartbeat_interval = {heartbeat_interval} - lambda_t = {lambda_t} - varphi_t = {varphi_t} - h_t = {h_t} - update_interval = {update_interval} - capture_interval = {capture_interval} - capture_dir = {capture_dir} - lead_time = {lead_time} - pan_gain = {pan_gain} - pan_rate_min = {pan_rate_min} - pan_rate_max = {pan_rate_max} - tilt_gain = {tilt_gain} - tilt_rate_min = {tilt_rate_min} - tilt_rate_max = {tilt_rate_max} - focus_slope = {focus_slope} - focus_intercept = {focus_intercept} - focus_min = {focus_min} - focus_max = {focus_max} - jpeg_resolution = {jpeg_resolution} - jpeg_compression = {jpeg_compression} - use_mqtt = {use_mqtt} - use_camera = {use_camera} - include_age = {include_age} - log_to_mqtt = {log_to_mqtt} - continue_on_exception = {continue_on_exception} - """ - ) + self._log_config() def decode_payload(self, payload: mqtt.MQTTMessage) -> Dict[Any, Any]: """ @@ -428,7 +396,8 @@ def _config_callback( None """ # Assign data attributes allowed to change during operation, - # ignoring config message data without a "camera" key + # ignoring config message data without a "axis-ptz-controller" + # key if type(msg) == mqtt.MQTTMessage: data = self.decode_payload(msg.payload) else: @@ -436,8 +405,18 @@ def _config_callback( if "axis-ptz-controller" not in data: return logging.info(f"Processing config msg data: {data}") - # TODO: Use module specific key? config = data["axis-ptz-controller"] + self.camera_ip = config.get("camera_ip", self.camera_ip) + self.camera_user = config.get("camera_user", self.camera_user) + self.camera_password = config.get("camera_password", self.camera_password) + self.config_topic = config.get("config_topic", self.config_topic) + self.orientation_topic = config.get("orientation_topic", self.orientation_topic) + self.object_topic = config.get("object_topic", self.object_topic) + self.capture_topic = config.get("capture_topic", self.capture_topic) + self.logger_topic = config.get("logger_topic", self.logger_topic) + self.heartbeat_interval = config.get( + "heartbeat_interval", self.heartbeat_interval + ) self.lambda_t = config.get("tripod_longitude", self.lambda_t) # [deg] self.varphi_t = config.get("tripod_latitude", self.varphi_t) # [deg] self.h_t = config.get("tripod_altitude", self.h_t) # [m] @@ -451,9 +430,28 @@ def _config_callback( self.lead_time = config.get("lead_time", self.lead_time) # [s] self.zoom = config.get("zoom", self.zoom) # [0-9999] self.pan_gain = config.get("pan_gain", self.pan_gain) # [1/s] + self.pan_rate_min = config.get("pan_rate_min", self.pan_rate_min) + self.pan_rate_max = config.get("pan_rate_max", self.pan_rate_max) self.tilt_gain = config.get("tilt_gain", self.tilt_gain) # [1/s] + self.tilt_gain = config.get("tilt_gain", self.tilt_gain) + self.tilt_rate_min = config.get("tilt_rate_min", self.tilt_rate_min) + self.tilt_rate_max = config.get("tilt_rate_max", self.tilt_rate_max) + self.focus_slope = config.get("focus_slope", self.focus_slope) + self.focus_intercept = config.get("focus_intercept", self.focus_intercept) + self.focus_min = config.get("focus_min", self.focus_min) + self.focus_max = config.get("focus_max", self.focus_max) + self.jpeg_resolution = config.get("jpeg_resolution", self.jpeg_resolution) + self.jpeg_compression = config.get("jpeg_compression", self.jpeg_compression) + self.use_mqtt = config.get("use_mqtt", self.use_mqtt) + self.use_camera = config.get("use_camera", self.use_camera) self.include_age = config.get("include_age", self.include_age) self.log_to_mqtt = config.get("log_to_mqtt", self.log_to_mqtt) + self.continue_on_exception = config.get( + "continue_on_exception", self.continue_on_exception + ) + + # Log configuration parameters + self._log_config() # Compute tripod position in the geocentric (XYZ) coordinate # system @@ -470,6 +468,47 @@ def _config_callback( self.e_z_XYZ, ) = axis_ptz_utilities.compute_E_XYZ_to_ENz(self.lambda_t, self.varphi_t) + def _log_config(self: Any) -> None: + """Logs all paramters that can be set on construction.""" + config = { + "camera_ip": self.camera_ip, + "camera_user": self.camera_user, + "camera_password": self.camera_password, + "config_topic": self.config_topic, + "orientation_topic": self.orientation_topic, + "object_topic": self.object_topic, + "capture_topic": self.capture_topic, + "logger_topic": self.logger_topic, + "heartbeat_interval": self.heartbeat_interval, + "lambda_t": self.lambda_t, + "varphi_t": self.varphi_t, + "h_t": self.h_t, + "update_interval": self.update_interval, + "capture_interval": self.capture_interval, + "capture_dir": self.capture_dir, + "lead_time": self.lead_time, + "pan_gain": self.pan_gain, + "pan_rate_min": self.pan_rate_min, + "pan_rate_max": self.pan_rate_max, + "tilt_gain": self.tilt_gain, + "tilt_rate_min": self.tilt_rate_min, + "tilt_rate_max": self.tilt_rate_max, + "focus_slope": self.focus_slope, + "focus_intercept": self.focus_intercept, + "focus_min": self.focus_min, + "focus_max": self.focus_max, + "jpeg_resolution": self.jpeg_resolution, + "jpeg_compression": self.jpeg_compression, + "use_mqtt": self.use_mqtt, + "use_camera": self.use_camera, + "include_age": self.include_age, + "log_to_mqtt": self.log_to_mqtt, + "continue_on_exception": self.continue_on_exception, + } + logging.info( + f"AxisPtzController configuration:\n{json.dumps(config, indent=4)}" + ) + def _orientation_callback( self, _client: Union[mqtt.Client, None], From 2a761e08f02f842c297c43210f798666ec8455ce Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Thu, 27 Jul 2023 15:15:23 +0000 Subject: [PATCH 04/56] Add unit test of image encoding --- axis-ptz-controller/axis_ptz_controller.py | 75 +++++++++--------- axis-ptz-controller/axis_ptz_utilities.py | 39 +++++++++ .../acc31a_97_1_2358_2023-06-14-15-32-59.jpg | Bin 0 -> 305301 bytes axis-ptz-controller/test_integration.py | 3 +- axis-ptz-controller/test_modules.py | 17 +++- 5 files changed, 93 insertions(+), 41 deletions(-) create mode 100644 axis-ptz-controller/data/acc31a_97_1_2358_2023-06-14-15-32-59.jpg diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index ff02dda..1616ffa 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -1,5 +1,4 @@ import ast -import base64 import coloredlogs from datetime import datetime import json @@ -795,7 +794,7 @@ def _publish_data(self: Any, data: Dict[str, str]) -> bool: deployment_id=os.environ.get( "DEPLOYMENT_ID", f"Unknown-Location-{self.hostname}" ), - current_location=os.envrion.get("CURRENT_LOCATION", "-90, -180"), + current_location=os.environ.get("CURRENT_LOCATION", "-90, -180"), status="Debug", message_type="Event", model_version="null", @@ -883,7 +882,6 @@ def _capture_image(self) -> None: None """ if self.do_capture: - # Capture an image in JPEG format self.capture_time = time() datetime_c = datetime.utcnow() @@ -912,8 +910,7 @@ def _capture_image(self) -> None: return # Publish the image after base64 encoding - with open(image_filepath, "rb") as image_file: - encoded_image = base64.b64encode(image_file.read()) + encoded_image = axis_ptz_utilities.encode_image(image_filepath) self._publish_data( { "type": "Encoded Image", @@ -1048,40 +1045,40 @@ def main(self) -> None: def make_controller() -> AxisPtzController: return AxisPtzController( - camera_ip=os.getenv("CAMERA_IP", ""), - camera_user=os.getenv("CAMERA_USER", ""), - camera_password=os.getenv("CAMERA_PASSWORD", ""), - mqtt_ip=os.getenv("MQTT_IP"), - config_topic=os.getenv("CONFIG_TOPIC", ""), - orientation_topic=os.getenv("ORIENTATION_TOPIC", ""), - object_topic=os.getenv("OBJECT_TOPIC", ""), - encoded_image_topic=os.getenv("ENCODED_IMAGE_TOPIC", ""), - image_metadata_topic=os.getenv("IMAGE_METADATA_TOPIC", ""), - logger_topic=os.getenv("LOGGER_TOPIC", ""), - heartbeat_interval=int(os.getenv("HEARTBEAT_INTERVAL", 10)), - lambda_t=float(os.getenv("TRIPOD_LONGITUDE", 0.0)), - varphi_t=float(os.getenv("TRIPOD_LATITUDE", 0.0)), - h_t=float(os.getenv("TRIPOD_ALTITUDE", 0.0)), - update_interval=float(os.getenv("UPDATE_INTERVAL", 0.1)), - capture_interval=int(os.getenv("CAPTURE_INTERVAL", 2)), - capture_dir=os.getenv("CAPTURE_DIR", "."), - lead_time=float(os.getenv("LEAD_TIME", 0.5)), - pan_gain=float(os.getenv("PAN_GAIN", 0.2)), - pan_rate_min=float(os.getenv("PAN_RATE_MIN", 1.8)), - pan_rate_max=float(os.getenv("PAN_RATE_MAX", 150.0)), - tilt_gain=float(os.getenv("TILT_GAIN", 0.2)), - tilt_rate_min=float(os.getenv("TILT_RATE_MIN", 1.8)), - tilt_rate_max=float(os.getenv("TILT_RATE_MAX", 150.0)), - focus_slope=float(os.getenv("FOCUS_SLOPE", 0.0006)), - focus_intercept=float(os.getenv("FOCUS_INTERCEPT", 54)), - focus_min=int(os.getenv("FOCUS_MIN", 5555)), - focus_max=int(os.getenv("FOCUS_MAX", 9999)), - jpeg_resolution=os.getenv("JPEG_RESOLUTION", "1920x1080"), - jpeg_compression=int(os.getenv("JPEG_COMPRESSION", 5)), - use_mqtt=ast.literal_eval(os.getenv("USE_MQTT", "True")), - use_camera=ast.literal_eval(os.getenv("USE_CAMERA", "True")), - include_age=ast.literal_eval(os.getenv("INCLUDE_AGE", "True")), - log_to_mqtt=ast.literal_eval(os.getenv("LOG_TO_MQTT", "False")), + camera_ip=os.environ.get("CAMERA_IP", ""), + camera_user=os.environ.get("CAMERA_USER", ""), + camera_password=os.environ.get("CAMERA_PASSWORD", ""), + mqtt_ip=os.environ.get("MQTT_IP"), + config_topic=os.environ.get("CONFIG_TOPIC", ""), + orientation_topic=os.environ.get("ORIENTATION_TOPIC", ""), + object_topic=os.environ.get("OBJECT_TOPIC", ""), + encoded_image_topic=os.environ.get("ENCODED_IMAGE_TOPIC", ""), + image_metadata_topic=os.environ.get("IMAGE_METADATA_TOPIC", ""), + logger_topic=os.environ.get("LOGGER_TOPIC", ""), + heartbeat_interval=int(os.environ.get("HEARTBEAT_INTERVAL", 10)), + lambda_t=float(os.environ.get("TRIPOD_LONGITUDE", 0.0)), + varphi_t=float(os.environ.get("TRIPOD_LATITUDE", 0.0)), + h_t=float(os.environ.get("TRIPOD_ALTITUDE", 0.0)), + update_interval=float(os.environ.get("UPDATE_INTERVAL", 0.1)), + capture_interval=int(os.environ.get("CAPTURE_INTERVAL", 2)), + capture_dir=os.environ.get("CAPTURE_DIR", "."), + lead_time=float(os.environ.get("LEAD_TIME", 0.5)), + pan_gain=float(os.environ.get("PAN_GAIN", 0.2)), + pan_rate_min=float(os.environ.get("PAN_RATE_MIN", 1.8)), + pan_rate_max=float(os.environ.get("PAN_RATE_MAX", 150.0)), + tilt_gain=float(os.environ.get("TILT_GAIN", 0.2)), + tilt_rate_min=float(os.environ.get("TILT_RATE_MIN", 1.8)), + tilt_rate_max=float(os.environ.get("TILT_RATE_MAX", 150.0)), + focus_slope=float(os.environ.get("FOCUS_SLOPE", 0.0006)), + focus_intercept=float(os.environ.get("FOCUS_INTERCEPT", 54)), + focus_min=int(os.environ.get("FOCUS_MIN", 5555)), + focus_max=int(os.environ.get("FOCUS_MAX", 9999)), + jpeg_resolution=os.environ.get("JPEG_RESOLUTION", "1920x1080"), + jpeg_compression=int(os.environ.get("JPEG_COMPRESSION", 5)), + use_mqtt=ast.literal_eval(os.environ.get("USE_MQTT", "True")), + use_camera=ast.literal_eval(os.environ.get("USE_CAMERA", "True")), + include_age=ast.literal_eval(os.environ.get("INCLUDE_AGE", "True")), + log_to_mqtt=ast.literal_eval(os.environ.get("LOG_TO_MQTT", "False")), ) diff --git a/axis-ptz-controller/axis_ptz_utilities.py b/axis-ptz-controller/axis_ptz_utilities.py index d1e3caa..7f7b382 100644 --- a/axis-ptz-controller/axis_ptz_utilities.py +++ b/axis-ptz-controller/axis_ptz_utilities.py @@ -1,9 +1,11 @@ +import base64 import coloredlogs import contextlib from datetime import datetime import logging import math import os +from pathlib import Path from typing import cast, Generator, Tuple, Union import numpy as np @@ -549,3 +551,40 @@ def pushd(new_dir: str) -> Generator[None, None, None]: yield finally: os.chdir(old_dir) + + +def encode_image(image_filepath: Path) -> str: + """Base64 encode an image from a file. + + Parameters + ---------- + image_filepath: Path + Path of the image file + + Returns + ------- + encoded_image: str + The Base64 encoded image as an ASCII string + """ + with open(image_filepath, "rb") as image_file: + encoded_image = base64.b64encode(image_file.read()).decode( + encoding="utf-8", errors="strict" + ) + return encoded_image + + +def decode_image(encoded_image: str) -> bytes: + """Base64 decode an image from an ASCII string. + + Parameters + ---------- + encoded_image: str + A Base64 encoded image as an ASCII string + + Returns + ------- + decoded_image: bytes + The Base64 decoded image + """ + decoded_image = base64.b64decode(encoded_image) + return decoded_image diff --git a/axis-ptz-controller/data/acc31a_97_1_2358_2023-06-14-15-32-59.jpg b/axis-ptz-controller/data/acc31a_97_1_2358_2023-06-14-15-32-59.jpg new file mode 100644 index 0000000000000000000000000000000000000000..eb946a2500658c65741731d71fa218da9f67e5ae GIT binary patch literal 305301 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�B4V@ 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�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

KzSWbQfkr5xVR?4$lW6OVb);)3o-wt!C*};j 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

;bMTuErou6_@qy4U=A6lw!0$H2bXO2Q9 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 axis_ptz_controller.AxisPtzController: config_topic=os.getenv("CONFIG_TOPIC", ""), orientation_topic=os.getenv("ORIENTATION_TOPIC", ""), object_topic=os.getenv("OBJECT_TOPIC", ""), - capture_topic=os.getenv("CAPTURE_TOPIC", ""), + encoded_image_topic=os.getenv("ENCODED_IMAGE_TOPIC", ""), + image_metadata_topic=os.getenv("IMAGE_METADATA_TOPIC", ""), logger_topic=os.getenv("LOGGER_TOPIC", ""), heartbeat_interval=HEARTBEAT_INTERVAL, update_interval=UPDATE_INTERVAL, diff --git a/axis-ptz-controller/test_modules.py b/axis-ptz-controller/test_modules.py index 5d3794e..8693e0a 100644 --- a/axis-ptz-controller/test_modules.py +++ b/axis-ptz-controller/test_modules.py @@ -1,6 +1,7 @@ import json import math import os +from pathlib import Path from typing import Any, Dict import numpy as np @@ -63,7 +64,8 @@ def controller() -> axis_ptz_controller.AxisPtzController: config_topic=os.getenv("CONFIG_TOPIC", ""), orientation_topic=os.getenv("ORIENTATION_TOPIC", ""), object_topic=os.getenv("OBJECT_TOPIC", ""), - capture_topic=os.getenv("CAPTURE_TOPIC", ""), + encoded_image_topic=os.getenv("ENCODED_IMAGE_TOPIC", ""), + image_metadata_topic=os.getenv("IMAGE_METADATA_TOPIC", ""), logger_topic=os.getenv("LOGGER_TOPIC", ""), heartbeat_interval=HEARTBEAT_INTERVAL, update_interval=UPDATE_INTERVAL, @@ -500,3 +502,16 @@ def test_great_circle_distance( lambda_1, varphi_1, lambda_2, varphi_2 ) assert math.fabs((d_act - d_exp) / d_exp) < PRECISION + + + # Base64 encode an image from a file + def test_encode_image(self): + + image_filepath = Path("data/acc31a_97_1_2358_2023-06-14-15-32-59.jpg") + with open(image_filepath, "rb") as image_file: + image = image_file.read() + + encoded_image = axis_ptz_utilities.encode_image(image_filepath) + decoded_image = axis_ptz_utilities.decode_image(encoded_image) + + assert(decoded_image == image) From 24471a11adebf62499f02eb03b68a07044353611 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 31 Jul 2023 11:29:55 -0400 Subject: [PATCH 05/56] Update dependencies --- axis-ptz-controller/poetry.lock | 803 ++++++++++++++++---------------- 1 file changed, 407 insertions(+), 396 deletions(-) diff --git a/axis-ptz-controller/poetry.lock b/axis-ptz-controller/poetry.lock index 3313a18..0a15a00 100644 --- a/axis-ptz-controller/poetry.lock +++ b/axis-ptz-controller/poetry.lock @@ -135,13 +135,13 @@ test = ["astroid", "pytest"] [[package]] name = "async-lru" -version = "2.0.3" +version = "2.0.4" description = "Simple LRU cache for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "async-lru-2.0.3.tar.gz", hash = "sha256:b714c9d1415fca4e264da72a9e2abc66880ce7430e03a973341f88ea4c0d4869"}, - {file = "async_lru-2.0.3-py3-none-any.whl", hash = "sha256:00c0a8899c20b9c88663a47732689ff98189c9fa08ad9f734d7722f934d250b1"}, + {file = "async-lru-2.0.4.tar.gz", hash = "sha256:b8a59a5df60805ff63220b2a0c5b5393da5521b113cd5465a44eb037d81a5627"}, + {file = "async_lru-2.0.4-py3-none-any.whl", hash = "sha256:ff02944ce3c288c5be660c42dbcca0742b32c3b279d6dceda655190240b99224"}, ] [[package]] @@ -266,13 +266,13 @@ css = ["tinycss2 (>=1.1.0,<1.2)"] [[package]] name = "certifi" -version = "2023.5.7" +version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, - {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, + {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, + {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, ] [[package]] @@ -437,13 +437,13 @@ files = [ [[package]] name = "click" -version = "8.1.5" +version = "8.1.6" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" files = [ - {file = "click-8.1.5-py3-none-any.whl", hash = "sha256:e576aa487d679441d7d30abb87e1b43d24fc53bffb8758443b1a9e1cee504548"}, - {file = "click-8.1.5.tar.gz", hash = "sha256:4be4b1af8d665c6d942909916d31a213a106800c47d0eeba73d34da3cbc11367"}, + {file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"}, + {file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"}, ] [package.dependencies] @@ -616,18 +616,29 @@ files = [ [[package]] name = "dill" -version = "0.3.6" -description = "serialize all of python" +version = "0.3.7" +description = "serialize all of Python" optional = false python-versions = ">=3.7" files = [ - {file = "dill-0.3.6-py3-none-any.whl", hash = "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0"}, - {file = "dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"}, + {file = "dill-0.3.7-py3-none-any.whl", hash = "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e"}, + {file = "dill-0.3.7.tar.gz", hash = "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03"}, ] [package.extras] graph = ["objgraph (>=1.7.2)"] +[[package]] +name = "entrypoints" +version = "0.4" +description = "Discover and load entry points from installed packages." +optional = false +python-versions = ">=3.6" +files = [ + {file = "entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f"}, + {file = "entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4"}, +] + [[package]] name = "executing" version = "1.2.0" @@ -644,13 +655,13 @@ tests = ["asttokens", "littleutils", "pytest", "rich"] [[package]] name = "fastjsonschema" -version = "2.17.1" +version = "2.18.0" description = "Fastest Python implementation of JSON schema" optional = false python-versions = "*" files = [ - {file = "fastjsonschema-2.17.1-py3-none-any.whl", hash = "sha256:4b90b252628ca695280924d863fe37234eebadc29c5360d322571233dc9746e0"}, - {file = "fastjsonschema-2.17.1.tar.gz", hash = "sha256:f4eeb8a77cef54861dbf7424ac8ce71306f12cbb086c45131bcba2c6a4f726e3"}, + {file = "fastjsonschema-2.18.0-py3-none-any.whl", hash = "sha256:128039912a11a807068a7c87d0da36660afbfd7202780db26c4aa7153cfdc799"}, + {file = "fastjsonschema-2.18.0.tar.gz", hash = "sha256:e820349dd16f806e4bd1467a138dced9def4bc7d6213a34295272a6cac95b5bd"}, ] [package.extras] @@ -658,61 +669,61 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc [[package]] name = "flake8" -version = "6.0.0" +version = "6.1.0" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-6.0.0-py2.py3-none-any.whl", hash = "sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7"}, - {file = "flake8-6.0.0.tar.gz", hash = "sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181"}, + {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, + {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, ] [package.dependencies] mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.10.0,<2.11.0" -pyflakes = ">=3.0.0,<3.1.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.1.0,<3.2.0" [[package]] name = "fonttools" -version = "4.41.0" +version = "4.41.1" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" files = [ - {file = "fonttools-4.41.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ba2a367ff478cd108d5319c0dc4fd4eb4ea3476dbfc45b00c45718e889cd9463"}, - {file = "fonttools-4.41.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:69178674505ec81adf4af2a3bbacd0cb9a37ba7831bc3fca307f80e48ab2767b"}, - {file = "fonttools-4.41.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86edb95c4d1fe4fae2111d7e0c10c6e42b7790b377bcf1952303469eee5b52bb"}, - {file = "fonttools-4.41.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50f8bdb421270f71b54695c62785e300fab4bb6127be40bf9f3084962a0c3adb"}, - {file = "fonttools-4.41.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c890061915e95b619c1d3cc3c107c6fb021406b701c0c24b03e74830d522f210"}, - {file = "fonttools-4.41.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b329ae7ce971b5c4148d6cdb8119c0ce4587265b2330d4f2f3776ef851bee020"}, - {file = "fonttools-4.41.0-cp310-cp310-win32.whl", hash = "sha256:bc9e7b1e268be7a23fc66471b615c324e99c5db39ce8c49dd6dd8e962c7bc1b8"}, - {file = "fonttools-4.41.0-cp310-cp310-win_amd64.whl", hash = "sha256:f3fe90dfb297bd8265238c06787911cd81c2cb89ac5b13e1c911928bdabfce0f"}, - {file = "fonttools-4.41.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e38bd91eae257f36c2b7245c0278e9cd9d754f3a66b8d2b548c623ba66e387b6"}, - {file = "fonttools-4.41.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:415cf7c806a3f56fb280dadcf3c92c85c0415e75665ca957b4a2a2e39c17a5c9"}, - {file = "fonttools-4.41.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:381558eafffc1432d08ca58063e71c7376ecaae48e9318354a90a1049a644845"}, - {file = "fonttools-4.41.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ee75b8ca48f6c48af25e967dce995ef94e46872b35c7d454b983c62c9c7006d"}, - {file = "fonttools-4.41.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d45f28c20bb67dee0f4a4caae709f40b0693d764b7b2bf2d58890f36b1bfcef0"}, - {file = "fonttools-4.41.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5448a87f6ed57ed844b64a05d3792827af584a8584613f6289867f4e77eb603b"}, - {file = "fonttools-4.41.0-cp311-cp311-win32.whl", hash = "sha256:69dbe0154e15b68dd671441ea8f23dad87488b24a6e650d45958f4722819a443"}, - {file = "fonttools-4.41.0-cp311-cp311-win_amd64.whl", hash = "sha256:ea879afd1d6189fca02a85a7868560c9bb8415dccff6b7ae6d81e4f06b3ab30d"}, - {file = "fonttools-4.41.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8f602dd5bcde7e4241419924f23c6f0d66723dd5408a58c3a2f781745c693f45"}, - {file = "fonttools-4.41.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:06eac087ea55b3ebb2207d93b5ac56c847163899f05f5a77e1910f688fe10030"}, - {file = "fonttools-4.41.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e22d0144d735f6c7df770509b8c0c33414bf460df0d5dddc98a159e5dbb10eb"}, - {file = "fonttools-4.41.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19d461c801b8904d201c6c38a99bfcfef673bfdfe0c7f026f582ef78896434e0"}, - {file = "fonttools-4.41.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:72d40a32d6443871ea0d147813caad58394b48729dfa4fbc45dcaac54f9506f2"}, - {file = "fonttools-4.41.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0614b6348866092d00df3dfb37e037fc06412ca67087de361a2777ea5ed62c16"}, - {file = "fonttools-4.41.0-cp38-cp38-win32.whl", hash = "sha256:e43f6c7f9ba4f9d29edee530e45f9aa162872ec9549398b85971477a99f2a806"}, - {file = "fonttools-4.41.0-cp38-cp38-win_amd64.whl", hash = "sha256:eb9dfa87152bd97019adc387b2f29ef6af601de4386f36570ca537ace96d96ed"}, - {file = "fonttools-4.41.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d2dae84a3d0f76884a6102c62f2795b2d6602c2c95cfcce74c8a590b6200e533"}, - {file = "fonttools-4.41.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cc3324e4159e6d1f55c3615b4c1c211f87cc96cc0cc7c946c8447dc1319f2e9d"}, - {file = "fonttools-4.41.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c654b1facf1f3b742e4d9b2dcdf0fa867b1f007b1b4981cc58a75ef5dca2a3c"}, - {file = "fonttools-4.41.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:560ea1a604c927399f36742abf342a4c5f3fee8e8e8a484b774dfe9630bd9a91"}, - {file = "fonttools-4.41.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9387b09694fbf8ac7dcf887069068f81fb4124d05e09557ac7daabfbec1744bd"}, - {file = "fonttools-4.41.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:465d0f24bf4f75160f441793b55076b7a080a57d3a1f738390af2c20bee24fbb"}, - {file = "fonttools-4.41.0-cp39-cp39-win32.whl", hash = "sha256:841c491fa3e9c54e8f9cd5dae059e88f45e086aea090c28be9d42f59c8b99e01"}, - {file = "fonttools-4.41.0-cp39-cp39-win_amd64.whl", hash = "sha256:efd59e83223cb77952997fb850c7a7c2a958c9af0642060f536722c2a9e9d53b"}, - {file = "fonttools-4.41.0-py3-none-any.whl", hash = "sha256:5b1c2b21b40229166a864f2b0aec06d37f0a204066deb1734c93370e0c76339d"}, - {file = "fonttools-4.41.0.tar.gz", hash = "sha256:6faff25991dec48f8cac882055a09ae1a29fd15bc160bc3d663e789e994664c2"}, + {file = "fonttools-4.41.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a7bbb290d13c6dd718ec2c3db46fe6c5f6811e7ea1e07f145fd8468176398224"}, + {file = "fonttools-4.41.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ec453a45778524f925a8f20fd26a3326f398bfc55d534e37bab470c5e415caa1"}, + {file = "fonttools-4.41.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2071267deaa6d93cb16288613419679c77220543551cbe61da02c93d92df72f"}, + {file = "fonttools-4.41.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e3334d51f0e37e2c6056e67141b2adabc92613a968797e2571ca8a03bd64773"}, + {file = "fonttools-4.41.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cac73bbef7734e78c60949da11c4903ee5837168e58772371bd42a75872f4f82"}, + {file = "fonttools-4.41.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:edee0900cf0eedb29d17c7876102d6e5a91ee333882b1f5abc83e85b934cadb5"}, + {file = "fonttools-4.41.1-cp310-cp310-win32.whl", hash = "sha256:2a22b2c425c698dcd5d6b0ff0b566e8e9663172118db6fd5f1941f9b8063da9b"}, + {file = "fonttools-4.41.1-cp310-cp310-win_amd64.whl", hash = "sha256:547ab36a799dded58a46fa647266c24d0ed43a66028cd1cd4370b246ad426cac"}, + {file = "fonttools-4.41.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:849ec722bbf7d3501a0e879e57dec1fc54919d31bff3f690af30bb87970f9784"}, + {file = "fonttools-4.41.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:38cdecd8f1fd4bf4daae7fed1b3170dfc1b523388d6664b2204b351820aa78a7"}, + {file = "fonttools-4.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ae64303ba670f8959fdaaa30ba0c2dabe75364fdec1caeee596c45d51ca3425"}, + {file = "fonttools-4.41.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f14f3ccea4cc7dd1b277385adf3c3bf18f9860f87eab9c2fb650b0af16800f55"}, + {file = "fonttools-4.41.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:33191f062549e6bb1a4782c22a04ebd37009c09360e2d6686ac5083774d06d95"}, + {file = "fonttools-4.41.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:704bccd69b0abb6fab9f5e4d2b75896afa48b427caa2c7988792a2ffce35b441"}, + {file = "fonttools-4.41.1-cp311-cp311-win32.whl", hash = "sha256:4edc795533421e98f60acee7d28fc8d941ff5ac10f44668c9c3635ad72ae9045"}, + {file = "fonttools-4.41.1-cp311-cp311-win_amd64.whl", hash = "sha256:aaaef294d8e411f0ecb778a0aefd11bb5884c9b8333cc1011bdaf3b58ca4bd75"}, + {file = "fonttools-4.41.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d1f9471134affc1e3b1b806db6e3e2ad3fa99439e332f1881a474c825101096"}, + {file = "fonttools-4.41.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:59eba8b2e749a1de85760da22333f3d17c42b66e03758855a12a2a542723c6e7"}, + {file = "fonttools-4.41.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9b3cc10dc9e0834b6665fd63ae0c6964c6bc3d7166e9bc84772e0edd09f9fa2"}, + {file = "fonttools-4.41.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2c2964bdc827ba6b8a91dc6de792620be4da3922c4cf0599f36a488c07e2b2"}, + {file = "fonttools-4.41.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7763316111df7b5165529f4183a334aa24c13cdb5375ffa1dc8ce309c8bf4e5c"}, + {file = "fonttools-4.41.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b2d1ee95be42b80d1f002d1ee0a51d7a435ea90d36f1a5ae331be9962ee5a3f1"}, + {file = "fonttools-4.41.1-cp38-cp38-win32.whl", hash = "sha256:f48602c0b3fd79cd83a34c40af565fe6db7ac9085c8823b552e6e751e3a5b8be"}, + {file = "fonttools-4.41.1-cp38-cp38-win_amd64.whl", hash = "sha256:b0938ebbeccf7c80bb9a15e31645cf831572c3a33d5cc69abe436e7000c61b14"}, + {file = "fonttools-4.41.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e5c2b0a95a221838991e2f0e455dec1ca3a8cc9cd54febd68cc64d40fdb83669"}, + {file = "fonttools-4.41.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:891cfc5a83b0307688f78b9bb446f03a7a1ad981690ac8362f50518bc6153975"}, + {file = "fonttools-4.41.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73ef0bb5d60eb02ba4d3a7d23ada32184bd86007cb2de3657cfcb1175325fc83"}, + {file = "fonttools-4.41.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f240d9adf0583ac8fc1646afe7f4ac039022b6f8fa4f1575a2cfa53675360b69"}, + {file = "fonttools-4.41.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bdd729744ae7ecd7f7311ad25d99da4999003dcfe43b436cf3c333d4e68de73d"}, + {file = "fonttools-4.41.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b927e5f466d99c03e6e20961946314b81d6e3490d95865ef88061144d9f62e38"}, + {file = "fonttools-4.41.1-cp39-cp39-win32.whl", hash = "sha256:afce2aeb80be72b4da7dd114f10f04873ff512793d13ce0b19d12b2a4c44c0f0"}, + {file = "fonttools-4.41.1-cp39-cp39-win_amd64.whl", hash = "sha256:1df1b6f4c7c4bc8201eb47f3b268adbf2539943aa43c400f84556557e3e109c0"}, + {file = "fonttools-4.41.1-py3-none-any.whl", hash = "sha256:952cb405f78734cf6466252fec42e206450d1a6715746013f64df9cbd4f896fa"}, + {file = "fonttools-4.41.1.tar.gz", hash = "sha256:e16a9449f21a93909c5be2f5ed5246420f2316e94195dbfccb5238aaa38f9751"}, ] [package.extras] @@ -778,13 +789,13 @@ files = [ [[package]] name = "ipykernel" -version = "6.24.0" +version = "6.25.0" description = "IPython Kernel for Jupyter" optional = false python-versions = ">=3.8" files = [ - {file = "ipykernel-6.24.0-py3-none-any.whl", hash = "sha256:2f5fffc7ad8f1fd5aadb4e171ba9129d9668dbafa374732cf9511ada52d6547f"}, - {file = "ipykernel-6.24.0.tar.gz", hash = "sha256:29cea0a716b1176d002a61d0b0c851f34536495bc4ef7dd0222c88b41b816123"}, + {file = "ipykernel-6.25.0-py3-none-any.whl", hash = "sha256:f0042e867ac3f6bca1679e6a88cbd6a58ed93a44f9d0866aecde6efe8de76659"}, + {file = "ipykernel-6.25.0.tar.gz", hash = "sha256:e342ce84712861be4b248c4a73472be4702c1b0dd77448bfd6bcfb3af9d5ddf9"}, ] [package.dependencies] @@ -905,21 +916,21 @@ requirements-deprecated-finder = ["pip-api", "pipreqs"] [[package]] name = "jedi" -version = "0.18.2" +version = "0.19.0" description = "An autocompletion tool for Python that can be used for text editors." optional = false python-versions = ">=3.6" files = [ - {file = "jedi-0.18.2-py2.py3-none-any.whl", hash = "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e"}, - {file = "jedi-0.18.2.tar.gz", hash = "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612"}, + {file = "jedi-0.19.0-py2.py3-none-any.whl", hash = "sha256:cb8ce23fbccff0025e9386b5cf85e892f94c9b822378f8da49970471335ac64e"}, + {file = "jedi-0.19.0.tar.gz", hash = "sha256:bcf9894f1753969cbac8022a8c2eaee06bfa3724e4192470aaffe7eb6272b0c4"}, ] [package.dependencies] -parso = ">=0.8.0,<0.9.0" +parso = ">=0.8.3,<0.9.0" [package.extras] docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] [[package]] @@ -961,17 +972,18 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" files = [ {file = "jsonpointer-2.4-py2.py3-none-any.whl", hash = "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a"}, + {file = "jsonpointer-2.4.tar.gz", hash = "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88"}, ] [[package]] name = "jsonschema" -version = "4.18.3" +version = "4.18.4" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" files = [ - {file = "jsonschema-4.18.3-py3-none-any.whl", hash = "sha256:aab78b34c2de001c6b692232f08c21a97b436fe18e0b817bf0511046924fceef"}, - {file = "jsonschema-4.18.3.tar.gz", hash = "sha256:64b7104d72efe856bea49ca4af37a14a9eba31b40bb7238179f3803130fd34d9"}, + {file = "jsonschema-4.18.4-py3-none-any.whl", hash = "sha256:971be834317c22daaa9132340a51c01b50910724082c2c1a2ac87eeec153a3fe"}, + {file = "jsonschema-4.18.4.tar.gz", hash = "sha256:fb3642735399fa958c0d2aad7057901554596c63349f4f6b283c493cf692a25d"}, ] [package.dependencies] @@ -994,13 +1006,13 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- [[package]] name = "jsonschema-specifications" -version = "2023.6.1" +version = "2023.7.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.8" files = [ - {file = "jsonschema_specifications-2023.6.1-py3-none-any.whl", hash = "sha256:3d2b82663aff01815f744bb5c7887e2121a63399b49b104a3c96145474d091d7"}, - {file = "jsonschema_specifications-2023.6.1.tar.gz", hash = "sha256:ca1c4dd059a9e7b34101cf5b3ab7ff1d18b139f35950d598d629837ef66e8f28"}, + {file = "jsonschema_specifications-2023.7.1-py3-none-any.whl", hash = "sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1"}, + {file = "jsonschema_specifications-2023.7.1.tar.gz", hash = "sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb"}, ] [package.dependencies] @@ -1008,25 +1020,27 @@ referencing = ">=0.28.0" [[package]] name = "jupyter-client" -version = "8.3.0" +version = "7.4.9" description = "Jupyter protocol implementation and client libraries" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "jupyter_client-8.3.0-py3-none-any.whl", hash = "sha256:7441af0c0672edc5d28035e92ba5e32fadcfa8a4e608a434c228836a89df6158"}, - {file = "jupyter_client-8.3.0.tar.gz", hash = "sha256:3af69921fe99617be1670399a0b857ad67275eefcfa291e2c81a160b7b650f5f"}, + {file = "jupyter_client-7.4.9-py3-none-any.whl", hash = "sha256:214668aaea208195f4c13d28eb272ba79f945fc0cf3f11c7092c20b2ca1980e7"}, + {file = "jupyter_client-7.4.9.tar.gz", hash = "sha256:52be28e04171f07aed8f20e1616a5a552ab9fee9cbbe6c1896ae170c3880d392"}, ] [package.dependencies] -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +entrypoints = "*" +jupyter-core = ">=4.9.2" +nest-asyncio = ">=1.5.4" python-dateutil = ">=2.8.2" pyzmq = ">=23.0" tornado = ">=6.2" -traitlets = ">=5.3" +traitlets = "*" [package.extras] -docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] +doc = ["ipykernel", "myst-parser", "sphinx (>=1.3.6)", "sphinx-rtd-theme", "sphinxcontrib-github-alt"] +test = ["codecov", "coverage", "ipykernel (>=6.12)", "ipython", "mypy", "pre-commit", "pytest", "pytest-asyncio (>=0.18)", "pytest-cov", "pytest-timeout"] [[package]] name = "jupyter-core" @@ -1050,19 +1064,20 @@ test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"] [[package]] name = "jupyter-events" -version = "0.6.3" +version = "0.7.0" description = "Jupyter Event System library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "jupyter_events-0.6.3-py3-none-any.whl", hash = "sha256:57a2749f87ba387cd1bfd9b22a0875b889237dbf2edc2121ebb22bde47036c17"}, - {file = "jupyter_events-0.6.3.tar.gz", hash = "sha256:9a6e9995f75d1b7146b436ea24d696ce3a35bfa8bfe45e0c33c334c79464d0b3"}, + {file = "jupyter_events-0.7.0-py3-none-any.whl", hash = "sha256:4753da434c13a37c3f3c89b500afa0c0a6241633441421f6adafe2fb2e2b924e"}, + {file = "jupyter_events-0.7.0.tar.gz", hash = "sha256:7be27f54b8388c03eefea123a4f79247c5b9381c49fb1cd48615ee191eb12615"}, ] [package.dependencies] -jsonschema = {version = ">=3.2.0", extras = ["format-nongpl"]} +jsonschema = {version = ">=4.18.0", extras = ["format-nongpl"]} python-json-logger = ">=2.0.4" pyyaml = ">=5.3" +referencing = "*" rfc3339-validator = "*" rfc3986-validator = ">=0.1.1" traitlets = ">=5.3" @@ -1070,7 +1085,7 @@ traitlets = ">=5.3" [package.extras] cli = ["click", "rich"] docs = ["jupyterlite-sphinx", "myst-parser", "pydata-sphinx-theme", "sphinxcontrib-spelling"] -test = ["click", "coverage", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.19.0)", "pytest-console-scripts", "pytest-cov", "rich"] +test = ["click", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.19.0)", "pytest-console-scripts", "rich"] [[package]] name = "jupyter-lsp" @@ -1184,13 +1199,13 @@ files = [ [[package]] name = "jupyterlab-server" -version = "2.23.0" +version = "2.24.0" description = "A set of server components for JupyterLab and JupyterLab like applications." optional = false python-versions = ">=3.7" files = [ - {file = "jupyterlab_server-2.23.0-py3-none-any.whl", hash = "sha256:a5ea2c839336a8ba7c38c8e7b2f24cedf919f0d439f4d2e606d9322013a95788"}, - {file = "jupyterlab_server-2.23.0.tar.gz", hash = "sha256:83c01aa4ad9451cd61b383e634d939ff713850f4640c0056b2cdb2b6211a74c7"}, + {file = "jupyterlab_server-2.24.0-py3-none-any.whl", hash = "sha256:5f077e142bb8dc9b843d960f940c513581bceca3793a0d80f9c67d9522c4e876"}, + {file = "jupyterlab_server-2.24.0.tar.gz", hash = "sha256:4e6f99e0a5579bbbc32e449c4dbb039561d4f1a7827d5733273ed56738f21f07"}, ] [package.dependencies] @@ -1205,7 +1220,7 @@ requests = ">=2.28" [package.extras] docs = ["autodoc-traits", "jinja2 (<3.2.0)", "mistune (<4)", "myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-copybutton", "sphinxcontrib-openapi (>0.8)"] openapi = ["openapi-core (>=0.16.1,<0.17.0)", "ruamel-yaml"] -test = ["hatch", "ipykernel", "jupyterlab-server[openapi]", "openapi-spec-validator (>=0.5.1,<0.6.0)", "pytest (>=7.0)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter[server] (>=0.6.2)", "pytest-timeout", "requests-mock", "sphinxcontrib-spelling", "strict-rfc3339", "werkzeug"] +test = ["hatch", "ipykernel", "jupyterlab-server[openapi]", "openapi-spec-validator (>=0.5.1,<0.7.0)", "pytest (>=7.0)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter[server] (>=0.6.2)", "pytest-timeout", "requests-mock", "sphinxcontrib-spelling", "strict-rfc3339", "werkzeug"] [[package]] name = "kiwisolver" @@ -1339,13 +1354,10 @@ files = [ {file = "lxml-4.9.3-cp27-cp27m-macosx_11_0_x86_64.whl", hash = "sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c"}, {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d"}, {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef"}, - {file = "lxml-4.9.3-cp27-cp27m-win32.whl", hash = "sha256:2c74524e179f2ad6d2a4f7caf70e2d96639c0954c943ad601a9e146c76408ed7"}, - {file = "lxml-4.9.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4f1026bc732b6a7f96369f7bfe1a4f2290fb34dce00d8644bc3036fb351a4ca1"}, {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb"}, {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e"}, {file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"}, {file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c"}, {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"}, {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"}, {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"}, @@ -1354,7 +1366,6 @@ files = [ {file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"}, {file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"}, {file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a"}, {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"}, {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"}, {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"}, @@ -1374,7 +1385,6 @@ files = [ {file = "lxml-4.9.3-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584"}, @@ -1384,7 +1394,6 @@ files = [ {file = "lxml-4.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4"}, @@ -1394,7 +1403,6 @@ files = [ {file = "lxml-4.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa"}, @@ -1404,7 +1412,6 @@ files = [ {file = "lxml-4.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96"}, {file = "lxml-4.9.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340"}, {file = "lxml-4.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b"}, {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da"}, {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e"}, {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d"}, @@ -1415,16 +1422,13 @@ files = [ {file = "lxml-4.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2"}, {file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"}, {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3"}, {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"}, {file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"}, {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d"}, {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"}, {file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"}, {file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"}, {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7"}, {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"}, {file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"}, {file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"}, @@ -1707,13 +1711,13 @@ test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>= [[package]] name = "nbconvert" -version = "7.6.0" +version = "7.7.3" description = "Converting Jupyter Notebooks" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "nbconvert-7.6.0-py3-none-any.whl", hash = "sha256:5a445c6794b0791984bc5436608fe2c066cb43c83920c7bc91bde3b765e9a264"}, - {file = "nbconvert-7.6.0.tar.gz", hash = "sha256:24fcf27efdef2b51d7f090cc5ce5a9b178766a55be513c4ebab08c91899ab550"}, + {file = "nbconvert-7.7.3-py3-none-any.whl", hash = "sha256:3022adadff3f86578a47fab7c2228bb3ca9c56a24345642a22f917f6168b48fc"}, + {file = "nbconvert-7.7.3.tar.gz", hash = "sha256:4a5996bf5f3cd16aa0431897ba1aa4c64842c2079f434b3dc6b8c4b252ef3355"}, ] [package.dependencies] @@ -1739,18 +1743,18 @@ docs = ["ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sp qtpdf = ["nbconvert[qtpng]"] qtpng = ["pyqtwebengine (>=5.15)"] serve = ["tornado (>=6.1)"] -test = ["ipykernel", "ipywidgets (>=7)", "pre-commit", "pytest", "pytest-dependency"] -webpdf = ["pyppeteer (>=1,<1.1)"] +test = ["flaky", "ipykernel", "ipywidgets (>=7)", "pre-commit", "pytest", "pytest-dependency"] +webpdf = ["playwright"] [[package]] name = "nbformat" -version = "5.9.1" +version = "5.9.2" description = "The Jupyter Notebook format" optional = false python-versions = ">=3.8" files = [ - {file = "nbformat-5.9.1-py3-none-any.whl", hash = "sha256:b7968ebf4811178a4108ee837eae1442e3f054132100f0359219e9ed1ce3ca45"}, - {file = "nbformat-5.9.1.tar.gz", hash = "sha256:3a7f52d040639cbd8a3890218c8b0ffb93211588c57446c90095e32ba5881b5d"}, + {file = "nbformat-5.9.2-py3-none-any.whl", hash = "sha256:1c5172d786a41b82bcfd0c23f9e6b6f072e8fb49c39250219e4acfff1efe89e9"}, + {file = "nbformat-5.9.2.tar.gz", hash = "sha256:5f98b5ba1997dff175e77e0c17d5c10a96eaed2cbd1de3533d1fc35d5e111192"}, ] [package.dependencies] @@ -1765,24 +1769,24 @@ test = ["pep440", "pre-commit", "pytest", "testpath"] [[package]] name = "nest-asyncio" -version = "1.5.6" +version = "1.5.7" description = "Patch asyncio to allow nested event loops" optional = false python-versions = ">=3.5" files = [ - {file = "nest_asyncio-1.5.6-py3-none-any.whl", hash = "sha256:b9a953fb40dceaa587d109609098db21900182b16440652454a146cffb06e8b8"}, - {file = "nest_asyncio-1.5.6.tar.gz", hash = "sha256:d267cc1ff794403f7df692964d1d2a3fa9418ffea2a3f6859a439ff482fef290"}, + {file = "nest_asyncio-1.5.7-py3-none-any.whl", hash = "sha256:5301c82941b550b3123a1ea772ba9a1c80bad3a182be8c1a5ae6ad3be57a9657"}, + {file = "nest_asyncio-1.5.7.tar.gz", hash = "sha256:6a80f7b98f24d9083ed24608977c09dd608d83f91cccc24c9d2cba6d10e01c10"}, ] [[package]] name = "notebook" -version = "6.5.4" +version = "6.5.5" description = "A web-based notebook environment for interactive computing" optional = false python-versions = ">=3.7" files = [ - {file = "notebook-6.5.4-py3-none-any.whl", hash = "sha256:dd17e78aefe64c768737b32bf171c1c766666a21cc79a44d37a1700771cab56f"}, - {file = "notebook-6.5.4.tar.gz", hash = "sha256:517209568bd47261e2def27a140e97d49070602eea0d226a696f42a7f16c9a4e"}, + {file = "notebook-6.5.5-py3-none-any.whl", hash = "sha256:171039245a5b1a8f8233165091210632c21250ce2a652daed38fe8f94389984f"}, + {file = "notebook-6.5.5.tar.gz", hash = "sha256:457caa1fa1c647395420945b2b7559f603eedbc9aeb2a59a0c286c8029e31efa"}, ] [package.dependencies] @@ -1790,14 +1794,14 @@ argon2-cffi = "*" ipykernel = "*" ipython-genutils = "*" jinja2 = "*" -jupyter-client = ">=5.3.4" +jupyter-client = ">=5.3.4,<8" jupyter-core = ">=4.6.1" nbclassic = ">=0.4.7" nbconvert = ">=5" nbformat = "*" nest-asyncio = ">=1.5" prometheus-client = "*" -pyzmq = ">=17" +pyzmq = ">=17,<25" Send2Trash = ">=1.8.0" terminado = ">=0.8.3" tornado = ">=6.1" @@ -1827,36 +1831,35 @@ test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync" [[package]] name = "numpy" -version = "1.25.1" +version = "1.25.2" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.9" files = [ - {file = "numpy-1.25.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:77d339465dff3eb33c701430bcb9c325b60354698340229e1dff97745e6b3efa"}, - {file = "numpy-1.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d736b75c3f2cb96843a5c7f8d8ccc414768d34b0a75f466c05f3a739b406f10b"}, - {file = "numpy-1.25.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a90725800caeaa160732d6b31f3f843ebd45d6b5f3eec9e8cc287e30f2805bf"}, - {file = "numpy-1.25.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c6c9261d21e617c6dc5eacba35cb68ec36bb72adcff0dee63f8fbc899362588"}, - {file = "numpy-1.25.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0def91f8af6ec4bb94c370e38c575855bf1d0be8a8fbfba42ef9c073faf2cf19"}, - {file = "numpy-1.25.1-cp310-cp310-win32.whl", hash = "sha256:fd67b306320dcadea700a8f79b9e671e607f8696e98ec255915c0c6d6b818503"}, - {file = "numpy-1.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:c1516db588987450b85595586605742879e50dcce923e8973f79529651545b57"}, - {file = "numpy-1.25.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b82655dd8efeea69dbf85d00fca40013d7f503212bc5259056244961268b66e"}, - {file = "numpy-1.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e8f6049c4878cb16960fbbfb22105e49d13d752d4d8371b55110941fb3b17800"}, - {file = "numpy-1.25.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41a56b70e8139884eccb2f733c2f7378af06c82304959e174f8e7370af112e09"}, - {file = "numpy-1.25.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5154b1a25ec796b1aee12ac1b22f414f94752c5f94832f14d8d6c9ac40bcca6"}, - {file = "numpy-1.25.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38eb6548bb91c421261b4805dc44def9ca1a6eef6444ce35ad1669c0f1a3fc5d"}, - {file = "numpy-1.25.1-cp311-cp311-win32.whl", hash = "sha256:791f409064d0a69dd20579345d852c59822c6aa087f23b07b1b4e28ff5880fcb"}, - {file = "numpy-1.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:c40571fe966393b212689aa17e32ed905924120737194b5d5c1b20b9ed0fb171"}, - {file = "numpy-1.25.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3d7abcdd85aea3e6cdddb59af2350c7ab1ed764397f8eec97a038ad244d2d105"}, - {file = "numpy-1.25.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1a180429394f81c7933634ae49b37b472d343cccb5bb0c4a575ac8bbc433722f"}, - {file = "numpy-1.25.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d412c1697c3853c6fc3cb9751b4915859c7afe6a277c2bf00acf287d56c4e625"}, - {file = "numpy-1.25.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20e1266411120a4f16fad8efa8e0454d21d00b8c7cee5b5ccad7565d95eb42dd"}, - {file = "numpy-1.25.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f76aebc3358ade9eacf9bc2bb8ae589863a4f911611694103af05346637df1b7"}, - {file = "numpy-1.25.1-cp39-cp39-win32.whl", hash = "sha256:247d3ffdd7775bdf191f848be8d49100495114c82c2bd134e8d5d075fb386a1c"}, - {file = "numpy-1.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:1d5d3c68e443c90b38fdf8ef40e60e2538a27548b39b12b73132456847f4b631"}, - {file = "numpy-1.25.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:35a9527c977b924042170a0887de727cd84ff179e478481404c5dc66b4170009"}, - {file = "numpy-1.25.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d3fe3dd0506a28493d82dc3cf254be8cd0d26f4008a417385cbf1ae95b54004"}, - {file = "numpy-1.25.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:012097b5b0d00a11070e8f2e261128c44157a8689f7dedcf35576e525893f4fe"}, - {file = "numpy-1.25.1.tar.gz", hash = "sha256:9a3a9f3a61480cc086117b426a8bd86869c213fc4072e606f01c4e4b66eb92bf"}, + {file = "numpy-1.25.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db3ccc4e37a6873045580d413fe79b68e47a681af8db2e046f1dacfa11f86eb3"}, + {file = "numpy-1.25.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:90319e4f002795ccfc9050110bbbaa16c944b1c37c0baeea43c5fb881693ae1f"}, + {file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe4a913e29b418d096e696ddd422d8a5d13ffba4ea91f9f60440a3b759b0187"}, + {file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f08f2e037bba04e707eebf4bc934f1972a315c883a9e0ebfa8a7756eabf9e357"}, + {file = "numpy-1.25.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bec1e7213c7cb00d67093247f8c4db156fd03075f49876957dca4711306d39c9"}, + {file = "numpy-1.25.2-cp310-cp310-win32.whl", hash = "sha256:7dc869c0c75988e1c693d0e2d5b26034644399dd929bc049db55395b1379e044"}, + {file = "numpy-1.25.2-cp310-cp310-win_amd64.whl", hash = "sha256:834b386f2b8210dca38c71a6e0f4fd6922f7d3fcff935dbe3a570945acb1b545"}, + {file = "numpy-1.25.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5462d19336db4560041517dbb7759c21d181a67cb01b36ca109b2ae37d32418"}, + {file = "numpy-1.25.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5652ea24d33585ea39eb6a6a15dac87a1206a692719ff45d53c5282e66d4a8f"}, + {file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d60fbae8e0019865fc4784745814cff1c421df5afee233db6d88ab4f14655a2"}, + {file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e7f0f7f6d0eee8364b9a6304c2845b9c491ac706048c7e8cf47b83123b8dbf"}, + {file = "numpy-1.25.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bb33d5a1cf360304754913a350edda36d5b8c5331a8237268c48f91253c3a364"}, + {file = "numpy-1.25.2-cp311-cp311-win32.whl", hash = "sha256:5883c06bb92f2e6c8181df7b39971a5fb436288db58b5a1c3967702d4278691d"}, + {file = "numpy-1.25.2-cp311-cp311-win_amd64.whl", hash = "sha256:5c97325a0ba6f9d041feb9390924614b60b99209a71a69c876f71052521d42a4"}, + {file = "numpy-1.25.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b79e513d7aac42ae918db3ad1341a015488530d0bb2a6abcbdd10a3a829ccfd3"}, + {file = "numpy-1.25.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb942bfb6f84df5ce05dbf4b46673ffed0d3da59f13635ea9b926af3deb76926"}, + {file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e0746410e73384e70d286f93abf2520035250aad8c5714240b0492a7302fdca"}, + {file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7806500e4f5bdd04095e849265e55de20d8cc4b661b038957354327f6d9b295"}, + {file = "numpy-1.25.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8b77775f4b7df768967a7c8b3567e309f617dd5e99aeb886fa14dc1a0791141f"}, + {file = "numpy-1.25.2-cp39-cp39-win32.whl", hash = "sha256:2792d23d62ec51e50ce4d4b7d73de8f67a2fd3ea710dcbc8563a51a03fb07b01"}, + {file = "numpy-1.25.2-cp39-cp39-win_amd64.whl", hash = "sha256:76b4115d42a7dfc5d485d358728cdd8719be33cc5ec6ec08632a5d6fca2ed380"}, + {file = "numpy-1.25.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a1329e26f46230bf77b02cc19e900db9b52f398d6722ca853349a782d4cff55"}, + {file = "numpy-1.25.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3abc71e8b6edba80a01a52e66d83c5d14433cbcd26a40c329ec7ed09f37901"}, + {file = "numpy-1.25.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1b9735c27cea5d995496f46a8b1cd7b408b3f34b6d50459d9ac8fe3a20cc17bf"}, ] [[package]] @@ -2060,13 +2063,13 @@ testing = ["docopt", "pytest (<6.0.0)"] [[package]] name = "pathspec" -version = "0.11.1" +version = "0.11.2" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.7" files = [ - {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, - {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, ] [[package]] @@ -2174,18 +2177,18 @@ files = [ [[package]] name = "platformdirs" -version = "3.8.1" +version = "3.10.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.8.1-py3-none-any.whl", hash = "sha256:cec7b889196b9144d088e4c57d9ceef7374f6c39694ad1577a0aab50d27ea28c"}, - {file = "platformdirs-3.8.1.tar.gz", hash = "sha256:f87ca4fcff7d2b0f81c6a748a77973d7af0f4d526f98f308477c3c436c74d528"}, + {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, + {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, ] [package.extras] -docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] [[package]] name = "pluggy" @@ -2281,15 +2284,26 @@ files = [ [package.extras] tests = ["pytest"] +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] + [[package]] name = "pycodestyle" -version = "2.10.0" +version = "2.11.0" description = "Python style guide checker" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pycodestyle-2.10.0-py2.py3-none-any.whl", hash = "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"}, - {file = "pycodestyle-2.10.0.tar.gz", hash = "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053"}, + {file = "pycodestyle-2.11.0-py2.py3-none-any.whl", hash = "sha256:5d1013ba8dc7895b548be5afb05740ca82454fd899971563d2ef625d090326f8"}, + {file = "pycodestyle-2.11.0.tar.gz", hash = "sha256:259bcc17857d8a8b3b4a2327324b79e5f020a13c16074670f9c8c8f872ea76d0"}, ] [[package]] @@ -2305,13 +2319,13 @@ files = [ [[package]] name = "pyflakes" -version = "3.0.1" +version = "3.1.0" description = "passive checker of Python programs" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pyflakes-3.0.1-py2.py3-none-any.whl", hash = "sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf"}, - {file = "pyflakes-3.0.1.tar.gz", hash = "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd"}, + {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, + {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, ] [[package]] @@ -2330,17 +2344,17 @@ plugins = ["importlib-metadata"] [[package]] name = "pylint" -version = "2.17.4" +version = "2.17.5" description = "python code static checker" optional = false python-versions = ">=3.7.2" files = [ - {file = "pylint-2.17.4-py3-none-any.whl", hash = "sha256:7a1145fb08c251bdb5cca11739722ce64a63db479283d10ce718b2460e54123c"}, - {file = "pylint-2.17.4.tar.gz", hash = "sha256:5dcf1d9e19f41f38e4e85d10f511e5b9c35e1aa74251bf95cdd8cb23584e2db1"}, + {file = "pylint-2.17.5-py3-none-any.whl", hash = "sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413"}, + {file = "pylint-2.17.5.tar.gz", hash = "sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252"}, ] [package.dependencies] -astroid = ">=2.15.4,<=2.17.0-dev0" +astroid = ">=2.15.6,<=2.17.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = {version = ">=0.3.6", markers = "python_version >= \"3.11\""} isort = ">=4.2.5,<6" @@ -2458,166 +2472,163 @@ files = [ [[package]] name = "pywinpty" -version = "2.0.10" +version = "2.0.11" description = "Pseudo terminal support for Windows from Python." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pywinpty-2.0.10-cp310-none-win_amd64.whl", hash = "sha256:4c7d06ad10f6e92bc850a467f26d98f4f30e73d2fe5926536308c6ae0566bc16"}, - {file = "pywinpty-2.0.10-cp311-none-win_amd64.whl", hash = "sha256:7ffbd66310b83e42028fc9df7746118978d94fba8c1ebf15a7c1275fdd80b28a"}, - {file = "pywinpty-2.0.10-cp37-none-win_amd64.whl", hash = "sha256:38cb924f2778b5751ef91a75febd114776b3af0ae411bc667be45dd84fc881d3"}, - {file = "pywinpty-2.0.10-cp38-none-win_amd64.whl", hash = "sha256:902d79444b29ad1833b8d5c3c9aabdfd428f4f068504430df18074007c8c0de8"}, - {file = "pywinpty-2.0.10-cp39-none-win_amd64.whl", hash = "sha256:3c46aef80dd50979aff93de199e4a00a8ee033ba7a03cadf0a91fed45f0c39d7"}, - {file = "pywinpty-2.0.10.tar.gz", hash = "sha256:cdbb5694cf8c7242c2ecfaca35c545d31fa5d5814c3d67a4e628f803f680ebea"}, + {file = "pywinpty-2.0.11-cp310-none-win_amd64.whl", hash = "sha256:452f10ac9ff8ab9151aa8cea9e491a9612a12250b1899278c6a56bc184afb47f"}, + {file = "pywinpty-2.0.11-cp311-none-win_amd64.whl", hash = "sha256:6701867d42aec1239bc0fedf49a336570eb60eb886e81763db77ea2b6c533cc3"}, + {file = "pywinpty-2.0.11-cp38-none-win_amd64.whl", hash = "sha256:0ffd287751ad871141dc9724de70ea21f7fc2ff1af50861e0d232cf70739d8c4"}, + {file = "pywinpty-2.0.11-cp39-none-win_amd64.whl", hash = "sha256:e4e7f023c28ca7aa8e1313e53ba80a4d10171fe27857b7e02f99882dfe3e8638"}, + {file = "pywinpty-2.0.11.tar.gz", hash = "sha256:e244cffe29a894876e2cd251306efd0d8d64abd5ada0a46150a4a71c0b9ad5c5"}, ] [[package]] name = "pyyaml" -version = "6.0" +version = "6.0.1" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.6" files = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, - {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, - {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] [[package]] name = "pyzmq" -version = "25.1.0" +version = "24.0.1" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.6" files = [ - {file = "pyzmq-25.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:1a6169e69034eaa06823da6a93a7739ff38716142b3596c180363dee729d713d"}, - {file = "pyzmq-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:19d0383b1f18411d137d891cab567de9afa609b214de68b86e20173dc624c101"}, - {file = "pyzmq-25.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1e931d9a92f628858a50f5bdffdfcf839aebe388b82f9d2ccd5d22a38a789dc"}, - {file = "pyzmq-25.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97d984b1b2f574bc1bb58296d3c0b64b10e95e7026f8716ed6c0b86d4679843f"}, - {file = "pyzmq-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:154bddda2a351161474b36dba03bf1463377ec226a13458725183e508840df89"}, - {file = "pyzmq-25.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cb6d161ae94fb35bb518b74bb06b7293299c15ba3bc099dccd6a5b7ae589aee3"}, - {file = "pyzmq-25.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:90146ab578931e0e2826ee39d0c948d0ea72734378f1898939d18bc9c823fcf9"}, - {file = "pyzmq-25.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:831ba20b660b39e39e5ac8603e8193f8fce1ee03a42c84ade89c36a251449d80"}, - {file = "pyzmq-25.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3a522510e3434e12aff80187144c6df556bb06fe6b9d01b2ecfbd2b5bfa5c60c"}, - {file = "pyzmq-25.1.0-cp310-cp310-win32.whl", hash = "sha256:be24a5867b8e3b9dd5c241de359a9a5217698ff616ac2daa47713ba2ebe30ad1"}, - {file = "pyzmq-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:5693dcc4f163481cf79e98cf2d7995c60e43809e325b77a7748d8024b1b7bcba"}, - {file = "pyzmq-25.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:13bbe36da3f8aaf2b7ec12696253c0bf6ffe05f4507985a8844a1081db6ec22d"}, - {file = "pyzmq-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:69511d604368f3dc58d4be1b0bad99b61ee92b44afe1cd9b7bd8c5e34ea8248a"}, - {file = "pyzmq-25.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a983c8694667fd76d793ada77fd36c8317e76aa66eec75be2653cef2ea72883"}, - {file = "pyzmq-25.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:332616f95eb400492103ab9d542b69d5f0ff628b23129a4bc0a2fd48da6e4e0b"}, - {file = "pyzmq-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58416db767787aedbfd57116714aad6c9ce57215ffa1c3758a52403f7c68cff5"}, - {file = "pyzmq-25.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:cad9545f5801a125f162d09ec9b724b7ad9b6440151b89645241d0120e119dcc"}, - {file = "pyzmq-25.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d6128d431b8dfa888bf51c22a04d48bcb3d64431caf02b3cb943269f17fd2994"}, - {file = "pyzmq-25.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2b15247c49d8cbea695b321ae5478d47cffd496a2ec5ef47131a9e79ddd7e46c"}, - {file = "pyzmq-25.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:442d3efc77ca4d35bee3547a8e08e8d4bb88dadb54a8377014938ba98d2e074a"}, - {file = "pyzmq-25.1.0-cp311-cp311-win32.whl", hash = "sha256:65346f507a815a731092421d0d7d60ed551a80d9b75e8b684307d435a5597425"}, - {file = "pyzmq-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8b45d722046fea5a5694cba5d86f21f78f0052b40a4bbbbf60128ac55bfcc7b6"}, - {file = "pyzmq-25.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f45808eda8b1d71308c5416ef3abe958f033fdbb356984fabbfc7887bed76b3f"}, - {file = "pyzmq-25.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b697774ea8273e3c0460cf0bba16cd85ca6c46dfe8b303211816d68c492e132"}, - {file = "pyzmq-25.1.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b324fa769577fc2c8f5efcd429cef5acbc17d63fe15ed16d6dcbac2c5eb00849"}, - {file = "pyzmq-25.1.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:5873d6a60b778848ce23b6c0ac26c39e48969823882f607516b91fb323ce80e5"}, - {file = "pyzmq-25.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:f0d9e7ba6a815a12c8575ba7887da4b72483e4cfc57179af10c9b937f3f9308f"}, - {file = "pyzmq-25.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:414b8beec76521358b49170db7b9967d6974bdfc3297f47f7d23edec37329b00"}, - {file = "pyzmq-25.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:01f06f33e12497dca86353c354461f75275a5ad9eaea181ac0dc1662da8074fa"}, - {file = "pyzmq-25.1.0-cp36-cp36m-win32.whl", hash = "sha256:b5a07c4f29bf7cb0164664ef87e4aa25435dcc1f818d29842118b0ac1eb8e2b5"}, - {file = "pyzmq-25.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:968b0c737797c1809ec602e082cb63e9824ff2329275336bb88bd71591e94a90"}, - {file = "pyzmq-25.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:47b915ba666c51391836d7ed9a745926b22c434efa76c119f77bcffa64d2c50c"}, - {file = "pyzmq-25.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5af31493663cf76dd36b00dafbc839e83bbca8a0662931e11816d75f36155897"}, - {file = "pyzmq-25.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5489738a692bc7ee9a0a7765979c8a572520d616d12d949eaffc6e061b82b4d1"}, - {file = "pyzmq-25.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1fc56a0221bdf67cfa94ef2d6ce5513a3d209c3dfd21fed4d4e87eca1822e3a3"}, - {file = "pyzmq-25.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:75217e83faea9edbc29516fc90c817bc40c6b21a5771ecb53e868e45594826b0"}, - {file = "pyzmq-25.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3830be8826639d801de9053cf86350ed6742c4321ba4236e4b5568528d7bfed7"}, - {file = "pyzmq-25.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3575699d7fd7c9b2108bc1c6128641a9a825a58577775ada26c02eb29e09c517"}, - {file = "pyzmq-25.1.0-cp37-cp37m-win32.whl", hash = "sha256:95bd3a998d8c68b76679f6b18f520904af5204f089beebb7b0301d97704634dd"}, - {file = "pyzmq-25.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:dbc466744a2db4b7ca05589f21ae1a35066afada2f803f92369f5877c100ef62"}, - {file = "pyzmq-25.1.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:3bed53f7218490c68f0e82a29c92335daa9606216e51c64f37b48eb78f1281f4"}, - {file = "pyzmq-25.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eb52e826d16c09ef87132c6e360e1879c984f19a4f62d8a935345deac43f3c12"}, - {file = "pyzmq-25.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ddbef8b53cd16467fdbfa92a712eae46dd066aa19780681a2ce266e88fbc7165"}, - {file = "pyzmq-25.1.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9301cf1d7fc1ddf668d0abbe3e227fc9ab15bc036a31c247276012abb921b5ff"}, - {file = "pyzmq-25.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e23a8c3b6c06de40bdb9e06288180d630b562db8ac199e8cc535af81f90e64b"}, - {file = "pyzmq-25.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4a82faae00d1eed4809c2f18b37f15ce39a10a1c58fe48b60ad02875d6e13d80"}, - {file = "pyzmq-25.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c8398a1b1951aaa330269c35335ae69744be166e67e0ebd9869bdc09426f3871"}, - {file = "pyzmq-25.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d40682ac60b2a613d36d8d3a0cd14fbdf8e7e0618fbb40aa9fa7b796c9081584"}, - {file = "pyzmq-25.1.0-cp38-cp38-win32.whl", hash = "sha256:33d5c8391a34d56224bccf74f458d82fc6e24b3213fc68165c98b708c7a69325"}, - {file = "pyzmq-25.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:c66b7ff2527e18554030319b1376d81560ca0742c6e0b17ff1ee96624a5f1afd"}, - {file = "pyzmq-25.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:af56229ea6527a849ac9fb154a059d7e32e77a8cba27e3e62a1e38d8808cb1a5"}, - {file = "pyzmq-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bdca18b94c404af6ae5533cd1bc310c4931f7ac97c148bbfd2cd4bdd62b96253"}, - {file = "pyzmq-25.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0b6b42f7055bbc562f63f3df3b63e3dd1ebe9727ff0f124c3aa7bcea7b3a00f9"}, - {file = "pyzmq-25.1.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4c2fc7aad520a97d64ffc98190fce6b64152bde57a10c704b337082679e74f67"}, - {file = "pyzmq-25.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be86a26415a8b6af02cd8d782e3a9ae3872140a057f1cadf0133de685185c02b"}, - {file = "pyzmq-25.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:851fb2fe14036cfc1960d806628b80276af5424db09fe5c91c726890c8e6d943"}, - {file = "pyzmq-25.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2a21fec5c3cea45421a19ccbe6250c82f97af4175bc09de4d6dd78fb0cb4c200"}, - {file = "pyzmq-25.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bad172aba822444b32eae54c2d5ab18cd7dee9814fd5c7ed026603b8cae2d05f"}, - {file = "pyzmq-25.1.0-cp39-cp39-win32.whl", hash = "sha256:4d67609b37204acad3d566bb7391e0ecc25ef8bae22ff72ebe2ad7ffb7847158"}, - {file = "pyzmq-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:71c7b5896e40720d30cd77a81e62b433b981005bbff0cb2f739e0f8d059b5d99"}, - {file = "pyzmq-25.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4cb27ef9d3bdc0c195b2dc54fcb8720e18b741624686a81942e14c8b67cc61a6"}, - {file = "pyzmq-25.1.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0c4fc2741e0513b5d5a12fe200d6785bbcc621f6f2278893a9ca7bed7f2efb7d"}, - {file = "pyzmq-25.1.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fc34fdd458ff77a2a00e3c86f899911f6f269d393ca5675842a6e92eea565bae"}, - {file = "pyzmq-25.1.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8751f9c1442624da391bbd92bd4b072def6d7702a9390e4479f45c182392ff78"}, - {file = "pyzmq-25.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:6581e886aec3135964a302a0f5eb68f964869b9efd1dbafdebceaaf2934f8a68"}, - {file = "pyzmq-25.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5482f08d2c3c42b920e8771ae8932fbaa0a67dff925fc476996ddd8155a170f3"}, - {file = "pyzmq-25.1.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7fbcafa3ea16d1de1f213c226005fea21ee16ed56134b75b2dede5a2129e62"}, - {file = "pyzmq-25.1.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:adecf6d02b1beab8d7c04bc36f22bb0e4c65a35eb0b4750b91693631d4081c70"}, - {file = "pyzmq-25.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6d39e42a0aa888122d1beb8ec0d4ddfb6c6b45aecb5ba4013c27e2f28657765"}, - {file = "pyzmq-25.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7018289b402ebf2b2c06992813523de61d4ce17bd514c4339d8f27a6f6809492"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9e68ae9864d260b18f311b68d29134d8776d82e7f5d75ce898b40a88df9db30f"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e21cc00e4debe8f54c3ed7b9fcca540f46eee12762a9fa56feb8512fd9057161"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f666ae327a6899ff560d741681fdcdf4506f990595201ed39b44278c471ad98"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f5efcc29056dfe95e9c9db0dfbb12b62db9c4ad302f812931b6d21dd04a9119"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:48e5e59e77c1a83162ab3c163fc01cd2eebc5b34560341a67421b09be0891287"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:108c96ebbd573d929740d66e4c3d1bdf31d5cde003b8dc7811a3c8c5b0fc173b"}, - {file = "pyzmq-25.1.0.tar.gz", hash = "sha256:80c41023465d36280e801564a69cbfce8ae85ff79b080e1913f6e90481fb8957"}, + {file = "pyzmq-24.0.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:28b119ba97129d3001673a697b7cce47fe6de1f7255d104c2f01108a5179a066"}, + {file = "pyzmq-24.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bcbebd369493d68162cddb74a9c1fcebd139dfbb7ddb23d8f8e43e6c87bac3a6"}, + {file = "pyzmq-24.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae61446166983c663cee42c852ed63899e43e484abf080089f771df4b9d272ef"}, + {file = "pyzmq-24.0.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87f7ac99b15270db8d53f28c3c7b968612993a90a5cf359da354efe96f5372b4"}, + {file = "pyzmq-24.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dca7c3956b03b7663fac4d150f5e6d4f6f38b2462c1e9afd83bcf7019f17913"}, + {file = "pyzmq-24.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8c78bfe20d4c890cb5580a3b9290f700c570e167d4cdcc55feec07030297a5e3"}, + {file = "pyzmq-24.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:48f721f070726cd2a6e44f3c33f8ee4b24188e4b816e6dd8ba542c8c3bb5b246"}, + {file = "pyzmq-24.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:afe1f3bc486d0ce40abb0a0c9adb39aed3bbac36ebdc596487b0cceba55c21c1"}, + {file = "pyzmq-24.0.1-cp310-cp310-win32.whl", hash = "sha256:3e6192dbcefaaa52ed81be88525a54a445f4b4fe2fffcae7fe40ebb58bd06bfd"}, + {file = "pyzmq-24.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:86de64468cad9c6d269f32a6390e210ca5ada568c7a55de8e681ca3b897bb340"}, + {file = "pyzmq-24.0.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:838812c65ed5f7c2bd11f7b098d2e5d01685a3f6d1f82849423b570bae698c00"}, + {file = "pyzmq-24.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dfb992dbcd88d8254471760879d48fb20836d91baa90f181c957122f9592b3dc"}, + {file = "pyzmq-24.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7abddb2bd5489d30ffeb4b93a428130886c171b4d355ccd226e83254fcb6b9ef"}, + {file = "pyzmq-24.0.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94010bd61bc168c103a5b3b0f56ed3b616688192db7cd5b1d626e49f28ff51b3"}, + {file = "pyzmq-24.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8242543c522d84d033fe79be04cb559b80d7eb98ad81b137ff7e0a9020f00ace"}, + {file = "pyzmq-24.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ccb94342d13e3bf3ffa6e62f95b5e3f0bc6bfa94558cb37f4b3d09d6feb536ff"}, + {file = "pyzmq-24.0.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6640f83df0ae4ae1104d4c62b77e9ef39be85ebe53f636388707d532bee2b7b8"}, + {file = "pyzmq-24.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a180dbd5ea5d47c2d3b716d5c19cc3fb162d1c8db93b21a1295d69585bfddac1"}, + {file = "pyzmq-24.0.1-cp311-cp311-win32.whl", hash = "sha256:624321120f7e60336be8ec74a172ae7fba5c3ed5bf787cc85f7e9986c9e0ebc2"}, + {file = "pyzmq-24.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:1724117bae69e091309ffb8255412c4651d3f6355560d9af312d547f6c5bc8b8"}, + {file = "pyzmq-24.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:15975747462ec49fdc863af906bab87c43b2491403ab37a6d88410635786b0f4"}, + {file = "pyzmq-24.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b947e264f0e77d30dcbccbb00f49f900b204b922eb0c3a9f0afd61aaa1cedc3d"}, + {file = "pyzmq-24.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ec91f1bad66f3ee8c6deb65fa1fe418e8ad803efedd69c35f3b5502f43bd1dc"}, + {file = "pyzmq-24.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:db03704b3506455d86ec72c3358a779e9b1d07b61220dfb43702b7b668edcd0d"}, + {file = "pyzmq-24.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e7e66b4e403c2836ac74f26c4b65d8ac0ca1eef41dfcac2d013b7482befaad83"}, + {file = "pyzmq-24.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:7a23ccc1083c260fa9685c93e3b170baba45aeed4b524deb3f426b0c40c11639"}, + {file = "pyzmq-24.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:fa0ae3275ef706c0309556061185dd0e4c4cd3b7d6f67ae617e4e677c7a41e2e"}, + {file = "pyzmq-24.0.1-cp36-cp36m-win32.whl", hash = "sha256:f01de4ec083daebf210531e2cca3bdb1608dbbbe00a9723e261d92087a1f6ebc"}, + {file = "pyzmq-24.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:de4217b9eb8b541cf2b7fde4401ce9d9a411cc0af85d410f9d6f4333f43640be"}, + {file = "pyzmq-24.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:78068e8678ca023594e4a0ab558905c1033b2d3e806a0ad9e3094e231e115a33"}, + {file = "pyzmq-24.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77c2713faf25a953c69cf0f723d1b7dd83827b0834e6c41e3fb3bbc6765914a1"}, + {file = "pyzmq-24.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bb4af15f305056e95ca1bd086239b9ebc6ad55e9f49076d27d80027f72752f6"}, + {file = "pyzmq-24.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0f14cffd32e9c4c73da66db97853a6aeceaac34acdc0fae9e5bbc9370281864c"}, + {file = "pyzmq-24.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0108358dab8c6b27ff6b985c2af4b12665c1bc659648284153ee501000f5c107"}, + {file = "pyzmq-24.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d66689e840e75221b0b290b0befa86f059fb35e1ee6443bce51516d4d61b6b99"}, + {file = "pyzmq-24.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ae08ac90aa8fa14caafc7a6251bd218bf6dac518b7bff09caaa5e781119ba3f2"}, + {file = "pyzmq-24.0.1-cp37-cp37m-win32.whl", hash = "sha256:8421aa8c9b45ea608c205db9e1c0c855c7e54d0e9c2c2f337ce024f6843cab3b"}, + {file = "pyzmq-24.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54d8b9c5e288362ec8595c1d98666d36f2070fd0c2f76e2b3c60fbad9bd76227"}, + {file = "pyzmq-24.0.1-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:acbd0a6d61cc954b9f535daaa9ec26b0a60a0d4353c5f7c1438ebc88a359a47e"}, + {file = "pyzmq-24.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:47b11a729d61a47df56346283a4a800fa379ae6a85870d5a2e1e4956c828eedc"}, + {file = "pyzmq-24.0.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abe6eb10122f0d746a0d510c2039ae8edb27bc9af29f6d1b05a66cc2401353ff"}, + {file = "pyzmq-24.0.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:07bec1a1b22dacf718f2c0e71b49600bb6a31a88f06527dfd0b5aababe3fa3f7"}, + {file = "pyzmq-24.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0d945a85b70da97ae86113faf9f1b9294efe66bd4a5d6f82f2676d567338b66"}, + {file = "pyzmq-24.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1b7928bb7580736ffac5baf814097be342ba08d3cfdfb48e52773ec959572287"}, + {file = "pyzmq-24.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b946da90dc2799bcafa682692c1d2139b2a96ec3c24fa9fc6f5b0da782675330"}, + {file = "pyzmq-24.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c8840f064b1fb377cffd3efeaad2b190c14d4c8da02316dae07571252d20b31f"}, + {file = "pyzmq-24.0.1-cp38-cp38-win32.whl", hash = "sha256:4854f9edc5208f63f0841c0c667260ae8d6846cfa233c479e29fdc85d42ebd58"}, + {file = "pyzmq-24.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:42d4f97b9795a7aafa152a36fe2ad44549b83a743fd3e77011136def512e6c2a"}, + {file = "pyzmq-24.0.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:52afb0ac962963fff30cf1be775bc51ae083ef4c1e354266ab20e5382057dd62"}, + {file = "pyzmq-24.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bad8210ad4df68c44ff3685cca3cda448ee46e20d13edcff8909eba6ec01ca4"}, + {file = "pyzmq-24.0.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dabf1a05318d95b1537fd61d9330ef4313ea1216eea128a17615038859da3b3b"}, + {file = "pyzmq-24.0.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5bd3d7dfd9cd058eb68d9a905dec854f86649f64d4ddf21f3ec289341386c44b"}, + {file = "pyzmq-24.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8012bce6836d3f20a6c9599f81dfa945f433dab4dbd0c4917a6fb1f998ab33d"}, + {file = "pyzmq-24.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c31805d2c8ade9b11feca4674eee2b9cce1fec3e8ddb7bbdd961a09dc76a80ea"}, + {file = "pyzmq-24.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3104f4b084ad5d9c0cb87445cc8cfd96bba710bef4a66c2674910127044df209"}, + {file = "pyzmq-24.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:df0841f94928f8af9c7a1f0aaaffba1fb74607af023a152f59379c01c53aee58"}, + {file = "pyzmq-24.0.1-cp39-cp39-win32.whl", hash = "sha256:a435ef8a3bd95c8a2d316d6e0ff70d0db524f6037411652803e118871d703333"}, + {file = "pyzmq-24.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:2032d9cb994ce3b4cba2b8dfae08c7e25bc14ba484c770d4d3be33c27de8c45b"}, + {file = "pyzmq-24.0.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bb5635c851eef3a7a54becde6da99485eecf7d068bd885ac8e6d173c4ecd68b0"}, + {file = "pyzmq-24.0.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:83ea1a398f192957cb986d9206ce229efe0ee75e3c6635baff53ddf39bd718d5"}, + {file = "pyzmq-24.0.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:941fab0073f0a54dc33d1a0460cb04e0d85893cb0c5e1476c785000f8b359409"}, + {file = "pyzmq-24.0.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e8f482c44ccb5884bf3f638f29bea0f8dc68c97e38b2061769c4cb697f6140d"}, + {file = "pyzmq-24.0.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:613010b5d17906c4367609e6f52e9a2595e35d5cc27d36ff3f1b6fa6e954d944"}, + {file = "pyzmq-24.0.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:65c94410b5a8355cfcf12fd600a313efee46ce96a09e911ea92cf2acf6708804"}, + {file = "pyzmq-24.0.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:20e7eeb1166087db636c06cae04a1ef59298627f56fb17da10528ab52a14c87f"}, + {file = "pyzmq-24.0.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2712aee7b3834ace51738c15d9ee152cc5a98dc7d57dd93300461b792ab7b43"}, + {file = "pyzmq-24.0.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a7c280185c4da99e0cc06c63bdf91f5b0b71deb70d8717f0ab870a43e376db8"}, + {file = "pyzmq-24.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:858375573c9225cc8e5b49bfac846a77b696b8d5e815711b8d4ba3141e6e8879"}, + {file = "pyzmq-24.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:80093b595921eed1a2cead546a683b9e2ae7f4a4592bb2ab22f70d30174f003a"}, + {file = "pyzmq-24.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f3f3154fde2b1ff3aa7b4f9326347ebc89c8ef425ca1db8f665175e6d3bd42f"}, + {file = "pyzmq-24.0.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abb756147314430bee5d10919b8493c0ccb109ddb7f5dfd2fcd7441266a25b75"}, + {file = "pyzmq-24.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e706bac34e9f50779cb8c39f10b53a4d15aebb97235643d3112ac20bd577b4"}, + {file = "pyzmq-24.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:687700f8371643916a1d2c61f3fdaa630407dd205c38afff936545d7b7466066"}, + {file = "pyzmq-24.0.1.tar.gz", hash = "sha256:216f5d7dbb67166759e59b0479bca82b8acf9bed6015b526b8eb10143fb08e77"}, ] [package.dependencies] cffi = {version = "*", markers = "implementation_name == \"pypy\""} +py = {version = "*", markers = "implementation_name == \"pypy\""} [[package]] name = "referencing" -version = "0.29.1" +version = "0.30.0" description = "JSON Referencing + Python" optional = false python-versions = ">=3.8" files = [ - {file = "referencing-0.29.1-py3-none-any.whl", hash = "sha256:d3c8f323ee1480095da44d55917cfb8278d73d6b4d5f677e3e40eb21314ac67f"}, - {file = "referencing-0.29.1.tar.gz", hash = "sha256:90cb53782d550ba28d2166ef3f55731f38397def8832baac5d45235f1995e35e"}, + {file = "referencing-0.30.0-py3-none-any.whl", hash = "sha256:c257b08a399b6c2f5a3510a50d28ab5dbc7bbde049bcaf954d43c446f83ab548"}, + {file = "referencing-0.30.0.tar.gz", hash = "sha256:47237742e990457f7512c7d27486394a9aadaf876cbfaa4be65b27b4f4d47c6b"}, ] [package.dependencies] @@ -2701,108 +2712,108 @@ files = [ [[package]] name = "rpds-py" -version = "0.8.10" +version = "0.9.2" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.8.10-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:93d06cccae15b3836247319eee7b6f1fdcd6c10dabb4e6d350d27bd0bdca2711"}, - {file = "rpds_py-0.8.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3816a890a6a9e9f1de250afa12ca71c9a7a62f2b715a29af6aaee3aea112c181"}, - {file = "rpds_py-0.8.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7c6304b894546b5a6bdc0fe15761fa53fe87d28527a7142dae8de3c663853e1"}, - {file = "rpds_py-0.8.10-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad3bfb44c8840fb4be719dc58e229f435e227fbfbe133dc33f34981ff622a8f8"}, - {file = "rpds_py-0.8.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14f1c356712f66653b777ecd8819804781b23dbbac4eade4366b94944c9e78ad"}, - {file = "rpds_py-0.8.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82bb361cae4d0a627006dadd69dc2f36b7ad5dc1367af9d02e296ec565248b5b"}, - {file = "rpds_py-0.8.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2e3c4f2a8e3da47f850d7ea0d7d56720f0f091d66add889056098c4b2fd576c"}, - {file = "rpds_py-0.8.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15a90d0ac11b4499171067ae40a220d1ca3cb685ec0acc356d8f3800e07e4cb8"}, - {file = "rpds_py-0.8.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:70bb9c8004b97b4ef7ae56a2aa56dfaa74734a0987c78e7e85f00004ab9bf2d0"}, - {file = "rpds_py-0.8.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d64f9f88d5203274a002b54442cafc9c7a1abff2a238f3e767b70aadf919b451"}, - {file = "rpds_py-0.8.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ccbbd276642788c4376fbe8d4e6c50f0fb4972ce09ecb051509062915891cbf0"}, - {file = "rpds_py-0.8.10-cp310-none-win32.whl", hash = "sha256:fafc0049add8043ad07ab5382ee80d80ed7e3699847f26c9a5cf4d3714d96a84"}, - {file = "rpds_py-0.8.10-cp310-none-win_amd64.whl", hash = "sha256:915031002c86a5add7c6fd4beb601b2415e8a1c956590a5f91d825858e92fe6e"}, - {file = "rpds_py-0.8.10-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:84eb541a44f7a18f07a6bfc48b95240739e93defe1fdfb4f2a295f37837945d7"}, - {file = "rpds_py-0.8.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f59996d0550894affaad8743e97b9b9c98f638b221fac12909210ec3d9294786"}, - {file = "rpds_py-0.8.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9adb5664b78fcfcd830000416c8cc69853ef43cb084d645b3f1f0296edd9bae"}, - {file = "rpds_py-0.8.10-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f96f3f98fbff7af29e9edf9a6584f3c1382e7788783d07ba3721790625caa43e"}, - {file = "rpds_py-0.8.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:376b8de737401050bd12810003d207e824380be58810c031f10ec563ff6aef3d"}, - {file = "rpds_py-0.8.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d1c2bc319428d50b3e0fa6b673ab8cc7fa2755a92898db3a594cbc4eeb6d1f7"}, - {file = "rpds_py-0.8.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73a1e48430f418f0ac3dfd87860e4cc0d33ad6c0f589099a298cb53724db1169"}, - {file = "rpds_py-0.8.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134ec8f14ca7dbc6d9ae34dac632cdd60939fe3734b5d287a69683c037c51acb"}, - {file = "rpds_py-0.8.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4b519bac7c09444dd85280fd60f28c6dde4389c88dddf4279ba9b630aca3bbbe"}, - {file = "rpds_py-0.8.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9cd57981d9fab04fc74438d82460f057a2419974d69a96b06a440822d693b3c0"}, - {file = "rpds_py-0.8.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:69d089c026f6a8b9d64a06ff67dc3be196707b699d7f6ca930c25f00cf5e30d8"}, - {file = "rpds_py-0.8.10-cp311-none-win32.whl", hash = "sha256:220bdcad2d2936f674650d304e20ac480a3ce88a40fe56cd084b5780f1d104d9"}, - {file = "rpds_py-0.8.10-cp311-none-win_amd64.whl", hash = "sha256:6c6a0225b8501d881b32ebf3f5807a08ad3685b5eb5f0a6bfffd3a6e039b2055"}, - {file = "rpds_py-0.8.10-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:e3d0cd3dff0e7638a7b5390f3a53057c4e347f4ef122ee84ed93fc2fb7ea4aa2"}, - {file = "rpds_py-0.8.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d77dff3a5aa5eedcc3da0ebd10ff8e4969bc9541aa3333a8d41715b429e99f47"}, - {file = "rpds_py-0.8.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41c89a366eae49ad9e65ed443a8f94aee762931a1e3723749d72aeac80f5ef2f"}, - {file = "rpds_py-0.8.10-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3793c21494bad1373da517001d0849eea322e9a049a0e4789e50d8d1329df8e7"}, - {file = "rpds_py-0.8.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:805a5f3f05d186c5d50de2e26f765ba7896d0cc1ac5b14ffc36fae36df5d2f10"}, - {file = "rpds_py-0.8.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b01b39ad5411563031ea3977bbbc7324d82b088e802339e6296f082f78f6115c"}, - {file = "rpds_py-0.8.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3f1e860be21f3e83011116a65e7310486300e08d9a3028e73e8d13bb6c77292"}, - {file = "rpds_py-0.8.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a13c8e56c46474cd5958d525ce6a9996727a83d9335684e41f5192c83deb6c58"}, - {file = "rpds_py-0.8.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:93d99f957a300d7a4ced41615c45aeb0343bb8f067c42b770b505de67a132346"}, - {file = "rpds_py-0.8.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:148b0b38d719c0760e31ce9285a9872972bdd7774969a4154f40c980e5beaca7"}, - {file = "rpds_py-0.8.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3cc5e5b5514796f45f03a568981971b12a3570f3de2e76114f7dc18d4b60a3c4"}, - {file = "rpds_py-0.8.10-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:e8e24b210a4deb5a7744971f8f77393005bae7f873568e37dfd9effe808be7f7"}, - {file = "rpds_py-0.8.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b41941583adce4242af003d2a8337b066ba6148ca435f295f31ac6d9e4ea2722"}, - {file = "rpds_py-0.8.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c490204e16bca4f835dba8467869fe7295cdeaa096e4c5a7af97f3454a97991"}, - {file = "rpds_py-0.8.10-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ee45cd1d84beed6cbebc839fd85c2e70a3a1325c8cfd16b62c96e2ffb565eca"}, - {file = "rpds_py-0.8.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a8ca409f1252e1220bf09c57290b76cae2f14723746215a1e0506472ebd7bdf"}, - {file = "rpds_py-0.8.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96b293c0498c70162effb13100624c5863797d99df75f2f647438bd10cbf73e4"}, - {file = "rpds_py-0.8.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4627520a02fccbd324b33c7a83e5d7906ec746e1083a9ac93c41ac7d15548c7"}, - {file = "rpds_py-0.8.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e39d7ab0c18ac99955b36cd19f43926450baba21e3250f053e0704d6ffd76873"}, - {file = "rpds_py-0.8.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ba9f1d1ebe4b63801977cec7401f2d41e888128ae40b5441270d43140efcad52"}, - {file = "rpds_py-0.8.10-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:802f42200d8caf7f25bbb2a6464cbd83e69d600151b7e3b49f49a47fa56b0a38"}, - {file = "rpds_py-0.8.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d19db6ba816e7f59fc806c690918da80a7d186f00247048cd833acdab9b4847b"}, - {file = "rpds_py-0.8.10-cp38-none-win32.whl", hash = "sha256:7947e6e2c2ad68b1c12ee797d15e5f8d0db36331200b0346871492784083b0c6"}, - {file = "rpds_py-0.8.10-cp38-none-win_amd64.whl", hash = "sha256:fa326b3505d5784436d9433b7980171ab2375535d93dd63fbcd20af2b5ca1bb6"}, - {file = "rpds_py-0.8.10-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7b38a9ac96eeb6613e7f312cd0014de64c3f07000e8bf0004ad6ec153bac46f8"}, - {file = "rpds_py-0.8.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c4d42e83ddbf3445e6514f0aff96dca511421ed0392d9977d3990d9f1ba6753c"}, - {file = "rpds_py-0.8.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b21575031478609db6dbd1f0465e739fe0e7f424a8e7e87610a6c7f68b4eb16"}, - {file = "rpds_py-0.8.10-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:574868858a7ff6011192c023a5289158ed20e3f3b94b54f97210a773f2f22921"}, - {file = "rpds_py-0.8.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae40f4a70a1f40939d66ecbaf8e7edc144fded190c4a45898a8cfe19d8fc85ea"}, - {file = "rpds_py-0.8.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37f7ee4dc86db7af3bac6d2a2cedbecb8e57ce4ed081f6464510e537589f8b1e"}, - {file = "rpds_py-0.8.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:695f642a3a5dbd4ad2ffbbacf784716ecd87f1b7a460843b9ddf965ccaeafff4"}, - {file = "rpds_py-0.8.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f43ab4cb04bde6109eb2555528a64dfd8a265cc6a9920a67dcbde13ef53a46c8"}, - {file = "rpds_py-0.8.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a11ab0d97be374efd04f640c04fe5c2d3dabc6dfb998954ea946ee3aec97056d"}, - {file = "rpds_py-0.8.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:92cf5b3ee60eef41f41e1a2cabca466846fb22f37fc580ffbcb934d1bcab225a"}, - {file = "rpds_py-0.8.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ceaac0c603bf5ac2f505a78b2dcab78d3e6b706be6596c8364b64cc613d208d2"}, - {file = "rpds_py-0.8.10-cp39-none-win32.whl", hash = "sha256:dd4f16e57c12c0ae17606c53d1b57d8d1c8792efe3f065a37cb3341340599d49"}, - {file = "rpds_py-0.8.10-cp39-none-win_amd64.whl", hash = "sha256:c03a435d26c3999c2a8642cecad5d1c4d10c961817536af52035f6f4ee2f5dd0"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:0da53292edafecba5e1d8c1218f99babf2ed0bf1c791d83c0ab5c29b57223068"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d20a8ed227683401cc508e7be58cba90cc97f784ea8b039c8cd01111e6043e0"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97cab733d303252f7c2f7052bf021a3469d764fc2b65e6dbef5af3cbf89d4892"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8c398fda6df361a30935ab4c4bccb7f7a3daef2964ca237f607c90e9f3fdf66f"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2eb4b08c45f8f8d8254cdbfacd3fc5d6b415d64487fb30d7380b0d0569837bf1"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7dfb1cbb895810fa2b892b68153c17716c6abaa22c7dc2b2f6dcf3364932a1c"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89c92b74e8bf6f53a6f4995fd52f4bd510c12f103ee62c99e22bc9e05d45583c"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e9c0683cb35a9b5881b41bc01d5568ffc667910d9dbc632a1fba4e7d59e98773"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:0eeb2731708207d0fe2619afe6c4dc8cb9798f7de052da891de5f19c0006c315"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:7495010b658ec5b52835f21d8c8b1a7e52e194c50f095d4223c0b96c3da704b1"}, - {file = "rpds_py-0.8.10-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:c72ebc22e70e04126158c46ba56b85372bc4d54d00d296be060b0db1671638a4"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:2cd3045e7f6375dda64ed7db1c5136826facb0159ea982f77d9cf6125025bd34"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:2418cf17d653d24ffb8b75e81f9f60b7ba1b009a23298a433a4720b2a0a17017"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a2edf8173ac0c7a19da21bc68818be1321998528b5e3f748d6ee90c0ba2a1fd"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f29b8c55fd3a2bc48e485e37c4e2df3317f43b5cc6c4b6631c33726f52ffbb3"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a7d20c1cf8d7b3960c5072c265ec47b3f72a0c608a9a6ee0103189b4f28d531"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:521fc8861a86ae54359edf53a15a05fabc10593cea7b3357574132f8427a5e5a"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5c191713e98e7c28800233f039a32a42c1a4f9a001a8a0f2448b07391881036"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:083df0fafe199371206111583c686c985dddaf95ab3ee8e7b24f1fda54515d09"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ed41f3f49507936a6fe7003985ea2574daccfef999775525d79eb67344e23767"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:2614c2732bf45de5c7f9e9e54e18bc78693fa2f635ae58d2895b7965e470378c"}, - {file = "rpds_py-0.8.10-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:c60528671d9d467009a6ec284582179f6b88651e83367d0ab54cb739021cd7de"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ee744fca8d1ea822480a2a4e7c5f2e1950745477143668f0b523769426060f29"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a38b9f526d0d6cbdaa37808c400e3d9f9473ac4ff64d33d9163fd05d243dbd9b"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60e0e86e870350e03b3e25f9b1dd2c6cc72d2b5f24e070249418320a6f9097b7"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f53f55a8852f0e49b0fc76f2412045d6ad9d5772251dea8f55ea45021616e7d5"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c493365d3fad241d52f096e4995475a60a80f4eba4d3ff89b713bc65c2ca9615"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:300eb606e6b94a7a26f11c8cc8ee59e295c6649bd927f91e1dbd37a4c89430b6"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a665f6f1a87614d1c3039baf44109094926dedf785e346d8b0a728e9cabd27a"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:927d784648211447201d4c6f1babddb7971abad922b32257ab74de2f2750fad0"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:c200b30dd573afa83847bed7e3041aa36a8145221bf0cfdfaa62d974d720805c"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:08166467258fd0240a1256fce272f689f2360227ee41c72aeea103e9e4f63d2b"}, - {file = "rpds_py-0.8.10-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:996cc95830de9bc22b183661d95559ec6b3cd900ad7bc9154c4cbf5be0c9b734"}, - {file = "rpds_py-0.8.10.tar.gz", hash = "sha256:13e643ce8ad502a0263397362fb887594b49cf84bf518d6038c16f235f2bcea4"}, + {file = "rpds_py-0.9.2-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:ab6919a09c055c9b092798ce18c6c4adf49d24d4d9e43a92b257e3f2548231e7"}, + {file = "rpds_py-0.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d55777a80f78dd09410bd84ff8c95ee05519f41113b2df90a69622f5540c4f8b"}, + {file = "rpds_py-0.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a216b26e5af0a8e265d4efd65d3bcec5fba6b26909014effe20cd302fd1138fa"}, + {file = "rpds_py-0.9.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29cd8bfb2d716366a035913ced99188a79b623a3512292963d84d3e06e63b496"}, + {file = "rpds_py-0.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44659b1f326214950a8204a248ca6199535e73a694be8d3e0e869f820767f12f"}, + {file = "rpds_py-0.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:745f5a43fdd7d6d25a53ab1a99979e7f8ea419dfefebcab0a5a1e9095490ee5e"}, + {file = "rpds_py-0.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a987578ac5214f18b99d1f2a3851cba5b09f4a689818a106c23dbad0dfeb760f"}, + {file = "rpds_py-0.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bf4151acb541b6e895354f6ff9ac06995ad9e4175cbc6d30aaed08856558201f"}, + {file = "rpds_py-0.9.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:03421628f0dc10a4119d714a17f646e2837126a25ac7a256bdf7c3943400f67f"}, + {file = "rpds_py-0.9.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:13b602dc3e8dff3063734f02dcf05111e887f301fdda74151a93dbbc249930fe"}, + {file = "rpds_py-0.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fae5cb554b604b3f9e2c608241b5d8d303e410d7dfb6d397c335f983495ce7f6"}, + {file = "rpds_py-0.9.2-cp310-none-win32.whl", hash = "sha256:47c5f58a8e0c2c920cc7783113df2fc4ff12bf3a411d985012f145e9242a2764"}, + {file = "rpds_py-0.9.2-cp310-none-win_amd64.whl", hash = "sha256:4ea6b73c22d8182dff91155af018b11aac9ff7eca085750455c5990cb1cfae6e"}, + {file = "rpds_py-0.9.2-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:e564d2238512c5ef5e9d79338ab77f1cbbda6c2d541ad41b2af445fb200385e3"}, + {file = "rpds_py-0.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f411330a6376fb50e5b7a3e66894e4a39e60ca2e17dce258d53768fea06a37bd"}, + {file = "rpds_py-0.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e7521f5af0233e89939ad626b15278c71b69dc1dfccaa7b97bd4cdf96536bb7"}, + {file = "rpds_py-0.9.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8d3335c03100a073883857e91db9f2e0ef8a1cf42dc0369cbb9151c149dbbc1b"}, + {file = "rpds_py-0.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d25b1c1096ef0447355f7293fbe9ad740f7c47ae032c2884113f8e87660d8f6e"}, + {file = "rpds_py-0.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a5d3fbd02efd9cf6a8ffc2f17b53a33542f6b154e88dd7b42ef4a4c0700fdad"}, + {file = "rpds_py-0.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5934e2833afeaf36bd1eadb57256239785f5af0220ed8d21c2896ec4d3a765f"}, + {file = "rpds_py-0.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:095b460e117685867d45548fbd8598a8d9999227e9061ee7f012d9d264e6048d"}, + {file = "rpds_py-0.9.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:91378d9f4151adc223d584489591dbb79f78814c0734a7c3bfa9c9e09978121c"}, + {file = "rpds_py-0.9.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:24a81c177379300220e907e9b864107614b144f6c2a15ed5c3450e19cf536fae"}, + {file = "rpds_py-0.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:de0b6eceb46141984671802d412568d22c6bacc9b230174f9e55fc72ef4f57de"}, + {file = "rpds_py-0.9.2-cp311-none-win32.whl", hash = "sha256:700375326ed641f3d9d32060a91513ad668bcb7e2cffb18415c399acb25de2ab"}, + {file = "rpds_py-0.9.2-cp311-none-win_amd64.whl", hash = "sha256:0766babfcf941db8607bdaf82569ec38107dbb03c7f0b72604a0b346b6eb3298"}, + {file = "rpds_py-0.9.2-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:b1440c291db3f98a914e1afd9d6541e8fc60b4c3aab1a9008d03da4651e67386"}, + {file = "rpds_py-0.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0f2996fbac8e0b77fd67102becb9229986396e051f33dbceada3debaacc7033f"}, + {file = "rpds_py-0.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f30d205755566a25f2ae0382944fcae2f350500ae4df4e795efa9e850821d82"}, + {file = "rpds_py-0.9.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:159fba751a1e6b1c69244e23ba6c28f879a8758a3e992ed056d86d74a194a0f3"}, + {file = "rpds_py-0.9.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1f044792e1adcea82468a72310c66a7f08728d72a244730d14880cd1dabe36b"}, + {file = "rpds_py-0.9.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9251eb8aa82e6cf88510530b29eef4fac825a2b709baf5b94a6094894f252387"}, + {file = "rpds_py-0.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01899794b654e616c8625b194ddd1e5b51ef5b60ed61baa7a2d9c2ad7b2a4238"}, + {file = "rpds_py-0.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0c43f8ae8f6be1d605b0465671124aa8d6a0e40f1fb81dcea28b7e3d87ca1e1"}, + {file = "rpds_py-0.9.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:207f57c402d1f8712618f737356e4b6f35253b6d20a324d9a47cb9f38ee43a6b"}, + {file = "rpds_py-0.9.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b52e7c5ae35b00566d244ffefba0f46bb6bec749a50412acf42b1c3f402e2c90"}, + {file = "rpds_py-0.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:978fa96dbb005d599ec4fd9ed301b1cc45f1a8f7982d4793faf20b404b56677d"}, + {file = "rpds_py-0.9.2-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:6aa8326a4a608e1c28da191edd7c924dff445251b94653988efb059b16577a4d"}, + {file = "rpds_py-0.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:aad51239bee6bff6823bbbdc8ad85136c6125542bbc609e035ab98ca1e32a192"}, + {file = "rpds_py-0.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd4dc3602370679c2dfb818d9c97b1137d4dd412230cfecd3c66a1bf388a196"}, + {file = "rpds_py-0.9.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd9da77c6ec1f258387957b754f0df60766ac23ed698b61941ba9acccd3284d1"}, + {file = "rpds_py-0.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:190ca6f55042ea4649ed19c9093a9be9d63cd8a97880106747d7147f88a49d18"}, + {file = "rpds_py-0.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:876bf9ed62323bc7dcfc261dbc5572c996ef26fe6406b0ff985cbcf460fc8a4c"}, + {file = "rpds_py-0.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa2818759aba55df50592ecbc95ebcdc99917fa7b55cc6796235b04193eb3c55"}, + {file = "rpds_py-0.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9ea4d00850ef1e917815e59b078ecb338f6a8efda23369677c54a5825dbebb55"}, + {file = "rpds_py-0.9.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:5855c85eb8b8a968a74dc7fb014c9166a05e7e7a8377fb91d78512900aadd13d"}, + {file = "rpds_py-0.9.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:14c408e9d1a80dcb45c05a5149e5961aadb912fff42ca1dd9b68c0044904eb32"}, + {file = "rpds_py-0.9.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:65a0583c43d9f22cb2130c7b110e695fff834fd5e832a776a107197e59a1898e"}, + {file = "rpds_py-0.9.2-cp38-none-win32.whl", hash = "sha256:71f2f7715935a61fa3e4ae91d91b67e571aeb5cb5d10331ab681256bda2ad920"}, + {file = "rpds_py-0.9.2-cp38-none-win_amd64.whl", hash = "sha256:674c704605092e3ebbbd13687b09c9f78c362a4bc710343efe37a91457123044"}, + {file = "rpds_py-0.9.2-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:07e2c54bef6838fa44c48dfbc8234e8e2466d851124b551fc4e07a1cfeb37260"}, + {file = "rpds_py-0.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f7fdf55283ad38c33e35e2855565361f4bf0abd02470b8ab28d499c663bc5d7c"}, + {file = "rpds_py-0.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:890ba852c16ace6ed9f90e8670f2c1c178d96510a21b06d2fa12d8783a905193"}, + {file = "rpds_py-0.9.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50025635ba8b629a86d9d5474e650da304cb46bbb4d18690532dd79341467846"}, + {file = "rpds_py-0.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:517cbf6e67ae3623c5127206489d69eb2bdb27239a3c3cc559350ef52a3bbf0b"}, + {file = "rpds_py-0.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0836d71ca19071090d524739420a61580f3f894618d10b666cf3d9a1688355b1"}, + {file = "rpds_py-0.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c439fd54b2b9053717cca3de9583be6584b384d88d045f97d409f0ca867d80f"}, + {file = "rpds_py-0.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f68996a3b3dc9335037f82754f9cdbe3a95db42bde571d8c3be26cc6245f2324"}, + {file = "rpds_py-0.9.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7d68dc8acded354c972116f59b5eb2e5864432948e098c19fe6994926d8e15c3"}, + {file = "rpds_py-0.9.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f963c6b1218b96db85fc37a9f0851eaf8b9040aa46dec112611697a7023da535"}, + {file = "rpds_py-0.9.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5a46859d7f947061b4010e554ccd1791467d1b1759f2dc2ec9055fa239f1bc26"}, + {file = "rpds_py-0.9.2-cp39-none-win32.whl", hash = "sha256:e07e5dbf8a83c66783a9fe2d4566968ea8c161199680e8ad38d53e075df5f0d0"}, + {file = "rpds_py-0.9.2-cp39-none-win_amd64.whl", hash = "sha256:682726178138ea45a0766907957b60f3a1bf3acdf212436be9733f28b6c5af3c"}, + {file = "rpds_py-0.9.2-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:196cb208825a8b9c8fc360dc0f87993b8b260038615230242bf18ec84447c08d"}, + {file = "rpds_py-0.9.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c7671d45530fcb6d5e22fd40c97e1e1e01965fc298cbda523bb640f3d923b387"}, + {file = "rpds_py-0.9.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83b32f0940adec65099f3b1c215ef7f1d025d13ff947975a055989cb7fd019a4"}, + {file = "rpds_py-0.9.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f67da97f5b9eac838b6980fc6da268622e91f8960e083a34533ca710bec8611"}, + {file = "rpds_py-0.9.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03975db5f103997904c37e804e5f340c8fdabbb5883f26ee50a255d664eed58c"}, + {file = "rpds_py-0.9.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:987b06d1cdb28f88a42e4fb8a87f094e43f3c435ed8e486533aea0bf2e53d931"}, + {file = "rpds_py-0.9.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c861a7e4aef15ff91233751619ce3a3d2b9e5877e0fcd76f9ea4f6847183aa16"}, + {file = "rpds_py-0.9.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:02938432352359805b6da099c9c95c8a0547fe4b274ce8f1a91677401bb9a45f"}, + {file = "rpds_py-0.9.2-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ef1f08f2a924837e112cba2953e15aacfccbbfcd773b4b9b4723f8f2ddded08e"}, + {file = "rpds_py-0.9.2-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:35da5cc5cb37c04c4ee03128ad59b8c3941a1e5cd398d78c37f716f32a9b7f67"}, + {file = "rpds_py-0.9.2-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:141acb9d4ccc04e704e5992d35472f78c35af047fa0cfae2923835d153f091be"}, + {file = "rpds_py-0.9.2-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:79f594919d2c1a0cc17d1988a6adaf9a2f000d2e1048f71f298b056b1018e872"}, + {file = "rpds_py-0.9.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:a06418fe1155e72e16dddc68bb3780ae44cebb2912fbd8bb6ff9161de56e1798"}, + {file = "rpds_py-0.9.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b2eb034c94b0b96d5eddb290b7b5198460e2d5d0c421751713953a9c4e47d10"}, + {file = "rpds_py-0.9.2-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b08605d248b974eb02f40bdcd1a35d3924c83a2a5e8f5d0fa5af852c4d960af"}, + {file = "rpds_py-0.9.2-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a0805911caedfe2736935250be5008b261f10a729a303f676d3d5fea6900c96a"}, + {file = "rpds_py-0.9.2-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab2299e3f92aa5417d5e16bb45bb4586171c1327568f638e8453c9f8d9e0f020"}, + {file = "rpds_py-0.9.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c8d7594e38cf98d8a7df25b440f684b510cf4627fe038c297a87496d10a174f"}, + {file = "rpds_py-0.9.2-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b9ec12ad5f0a4625db34db7e0005be2632c1013b253a4a60e8302ad4d462afd"}, + {file = "rpds_py-0.9.2-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1fcdee18fea97238ed17ab6478c66b2095e4ae7177e35fb71fbe561a27adf620"}, + {file = "rpds_py-0.9.2-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:933a7d5cd4b84f959aedeb84f2030f0a01d63ae6cf256629af3081cf3e3426e8"}, + {file = "rpds_py-0.9.2-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:686ba516e02db6d6f8c279d1641f7067ebb5dc58b1d0536c4aaebb7bf01cdc5d"}, + {file = "rpds_py-0.9.2-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:0173c0444bec0a3d7d848eaeca2d8bd32a1b43f3d3fde6617aac3731fa4be05f"}, + {file = "rpds_py-0.9.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:d576c3ef8c7b2d560e301eb33891d1944d965a4d7a2eacb6332eee8a71827db6"}, + {file = "rpds_py-0.9.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed89861ee8c8c47d6beb742a602f912b1bb64f598b1e2f3d758948721d44d468"}, + {file = "rpds_py-0.9.2-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1054a08e818f8e18910f1bee731583fe8f899b0a0a5044c6e680ceea34f93876"}, + {file = "rpds_py-0.9.2-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99e7c4bb27ff1aab90dcc3e9d37ee5af0231ed98d99cb6f5250de28889a3d502"}, + {file = "rpds_py-0.9.2-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c545d9d14d47be716495076b659db179206e3fd997769bc01e2d550eeb685596"}, + {file = "rpds_py-0.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9039a11bca3c41be5a58282ed81ae422fa680409022b996032a43badef2a3752"}, + {file = "rpds_py-0.9.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fb39aca7a64ad0c9490adfa719dbeeb87d13be137ca189d2564e596f8ba32c07"}, + {file = "rpds_py-0.9.2-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2d8b3b3a2ce0eaa00c5bbbb60b6713e94e7e0becab7b3db6c5c77f979e8ed1f1"}, + {file = "rpds_py-0.9.2-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:99b1c16f732b3a9971406fbfe18468592c5a3529585a45a35adbc1389a529a03"}, + {file = "rpds_py-0.9.2-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:c27ee01a6c3223025f4badd533bea5e87c988cb0ba2811b690395dfe16088cfe"}, + {file = "rpds_py-0.9.2.tar.gz", hash = "sha256:8d70e8f14900f2657c249ea4def963bed86a29b81f81f5b76b5a9215680de945"}, ] [[package]] @@ -2941,13 +2952,13 @@ test = ["flake8", "isort", "pytest"] [[package]] name = "tomlkit" -version = "0.11.8" +version = "0.12.1" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ - {file = "tomlkit-0.11.8-py3-none-any.whl", hash = "sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171"}, - {file = "tomlkit-0.11.8.tar.gz", hash = "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"}, + {file = "tomlkit-0.12.1-py3-none-any.whl", hash = "sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899"}, + {file = "tomlkit-0.12.1.tar.gz", hash = "sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86"}, ] [[package]] @@ -3023,13 +3034,13 @@ dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake [[package]] name = "urllib3" -version = "2.0.3" +version = "2.0.4" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.7" files = [ - {file = "urllib3-2.0.3-py3-none-any.whl", hash = "sha256:48e7fafa40319d358848e1bc6809b208340fafe2096f1725d05d67443d0483d1"}, - {file = "urllib3-2.0.3.tar.gz", hash = "sha256:bee28b5e56addb8226c96f7f13ac28cb4c301dd5ea8a6ca179c0b9835e032825"}, + {file = "urllib3-2.0.4-py3-none-any.whl", hash = "sha256:de7df1803967d2c2a98e4b11bb7d6bd9210474c46e8a0401514e3a42a75ebde4"}, + {file = "urllib3-2.0.4.tar.gz", hash = "sha256:8d22f86aae8ef5e410d4f539fde9ce6b2113a001bb4d189e0aed70642d602b11"}, ] [package.extras] From 029d3fd7e08f4093ec184735c355a8f95c022159 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 31 Jul 2023 11:30:13 -0400 Subject: [PATCH 06/56] Update data for test messages --- axis-ptz-controller/data/config_msg.json | 10 ---------- axis-ptz-controller/data/config_msg_data.json | 7 +++++++ axis-ptz-controller/data/object_msg.json | 14 -------------- axis-ptz-controller/data/object_msg_data.json | 11 +++++++++++ axis-ptz-controller/data/orientation_msg_0s.json | 10 ---------- axis-ptz-controller/data/orientation_msg_90s.json | 10 ---------- .../data/orientation_msg_data_0s.json | 5 +++++ .../data/orientation_msg_data_90s.json | 5 +++++ 8 files changed, 28 insertions(+), 44 deletions(-) delete mode 100644 axis-ptz-controller/data/config_msg.json create mode 100644 axis-ptz-controller/data/config_msg_data.json delete mode 100644 axis-ptz-controller/data/object_msg.json create mode 100644 axis-ptz-controller/data/object_msg_data.json delete mode 100644 axis-ptz-controller/data/orientation_msg_0s.json delete mode 100644 axis-ptz-controller/data/orientation_msg_90s.json create mode 100644 axis-ptz-controller/data/orientation_msg_data_0s.json create mode 100644 axis-ptz-controller/data/orientation_msg_data_90s.json diff --git a/axis-ptz-controller/data/config_msg.json b/axis-ptz-controller/data/config_msg.json deleted file mode 100644 index 1e06e7d..0000000 --- a/axis-ptz-controller/data/config_msg.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "timestamp": "2023-01-01-00-00-00", - "data": { - "axis-ptz-controller": { - "tripod_longitude": 270.0, - "tripod_latitude": 90.0, - "tripod_altitude": 0.0 - } - } -} diff --git a/axis-ptz-controller/data/config_msg_data.json b/axis-ptz-controller/data/config_msg_data.json new file mode 100644 index 0000000..67635b4 --- /dev/null +++ b/axis-ptz-controller/data/config_msg_data.json @@ -0,0 +1,7 @@ +{ + "axis-ptz-controller": { + "tripod_longitude": 270.0, + "tripod_latitude": 90.0, + "tripod_altitude": 0.0 + } +} diff --git a/axis-ptz-controller/data/object_msg.json b/axis-ptz-controller/data/object_msg.json deleted file mode 100644 index e35e362..0000000 --- a/axis-ptz-controller/data/object_msg.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "timestamp": "2023-01-01-00-00-00", - "data": { - "object_id": "a1e946", - "object_type": "aircraft", - "timestamp": 1.0, - "latitude": 89.99, - "longitude": 270.0, - "altitude": 1000.0, - "track": 0.0, - "horizontal_velocity": -74.4999211024472, - "vertical_velocity": 66.7065345804228 - } -} diff --git a/axis-ptz-controller/data/object_msg_data.json b/axis-ptz-controller/data/object_msg_data.json new file mode 100644 index 0000000..93c2f5a --- /dev/null +++ b/axis-ptz-controller/data/object_msg_data.json @@ -0,0 +1,11 @@ +{ + "object_id": "a1e946", + "object_type": "aircraft", + "timestamp": 1.0, + "latitude": 89.99, + "longitude": 270.0, + "altitude": 1000.0, + "track": 0.0, + "horizontal_velocity": -74.4999211024472, + "vertical_velocity": 66.7065345804228 +} diff --git a/axis-ptz-controller/data/orientation_msg_0s.json b/axis-ptz-controller/data/orientation_msg_0s.json deleted file mode 100644 index 897c222..0000000 --- a/axis-ptz-controller/data/orientation_msg_0s.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "timestamp": "2023-01-01-00-00-00", - "data": { - "camera": { - "tripod_yaw": 0.0, - "tripod_pitch": 0.0, - "tripod_roll": 0.0 - } - } -} diff --git a/axis-ptz-controller/data/orientation_msg_90s.json b/axis-ptz-controller/data/orientation_msg_90s.json deleted file mode 100644 index 044632c..0000000 --- a/axis-ptz-controller/data/orientation_msg_90s.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "timestamp": "2023-01-01-00-00-00", - "data": { - "camera": { - "tripod_yaw": 90.0, - "tripod_pitch": 90.0, - "tripod_roll": 90.0 - } - } -} diff --git a/axis-ptz-controller/data/orientation_msg_data_0s.json b/axis-ptz-controller/data/orientation_msg_data_0s.json new file mode 100644 index 0000000..5f90a0f --- /dev/null +++ b/axis-ptz-controller/data/orientation_msg_data_0s.json @@ -0,0 +1,5 @@ +{ + "tripod_yaw": 0.0, + "tripod_pitch": 0.0, + "tripod_roll": 0.0 +} diff --git a/axis-ptz-controller/data/orientation_msg_data_90s.json b/axis-ptz-controller/data/orientation_msg_data_90s.json new file mode 100644 index 0000000..3a5dd3c --- /dev/null +++ b/axis-ptz-controller/data/orientation_msg_data_90s.json @@ -0,0 +1,5 @@ +{ + "tripod_yaw": 90.0, + "tripod_pitch": 90.0, + "tripod_roll": 90.0 +} From 6c97fadb6d6e494609a6b70d576212c49c423cd3 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 31 Jul 2023 11:31:00 -0400 Subject: [PATCH 07/56] Use base_mqtt_pub_sub.generate_payload_json() method consistently Resolve a myriad of resulting issues, including typingd, sigh. --- axis-ptz-controller/axis_ptz_controller.py | 163 ++++++++++++-------- axis-ptz-controller/axis_ptz_utilities.py | 2 +- axis-ptz-controller/camera_configuration.py | 2 +- axis-ptz-controller/camera_control.py | 2 +- axis-ptz-controller/test_integration.py | 161 +++++++++++-------- axis-ptz-controller/test_messages.py | 139 +++++++++++------ axis-ptz-controller/test_modules.py | 118 ++++++++++---- 7 files changed, 372 insertions(+), 215 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 1616ffa..fa726e1 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -1,5 +1,4 @@ import ast -import coloredlogs from datetime import datetime import json import logging @@ -14,6 +13,7 @@ from types import FrameType from typing import Any, Dict, Optional, Union +import coloredlogs import numpy as np import quaternion import paho.mqtt.client as mqtt @@ -51,6 +51,7 @@ class AxisPtzController(BaseMQTTPubSub): def __init__( self, + hostname: str, camera_ip: str, camera_user: str, camera_password: str, @@ -91,6 +92,7 @@ def __init__( Parameters ---------- + hostname (str): Name of host camera_ip: str Camera IP address camera_user: str @@ -165,6 +167,7 @@ def __init__( """ # Parent class handles kwargs, including MQTT IP super().__init__(**kwargs) + self.hostname = hostname self.camera_ip = camera_ip self.camera_user = camera_user self.camera_password = camera_password @@ -304,30 +307,55 @@ def __init__( # coordinate system, orthogonal transformation matrix from # geocentric (XYZ) to topocentric (ENz) coordinates, and East, # North, and zenith unit vectors - config_msg = { - "data": { - "axis-ptz-controller": { - "lambda_t": self.lambda_t, - "varphi_t": self.varphi_t, - "h_t": self.h_t, + config_msg = self.generate_payload_json( + push_timestamp=int(datetime.utcnow().timestamp()), + device_type=os.environ.get("DEVICE_TYPE", "Collector"), + id_=self.hostname, + deployment_id=os.environ.get( + "DEPLOYMENT_ID", f"Unknown-Location-{self.hostname}" + ), + current_location=os.environ.get("CURRENT_LOCATION", "-90, -180"), + status="Debug", + message_type="Event", + model_version="null", + firmware_version="v0.0.0", + data_payload_type="Configuration", + data_payload=json.dumps( + { + "axis-ptz-controller": { + "tripod_longitude": self.lambda_t, + "tripod_latitude": self.varphi_t, + "tripod_altitude": self.h_t, + } } - } - } + ), + ) self._config_callback(None, None, config_msg) # Initialize the rotations from the geocentric (XYZ) # coordinate system to the camera housing fixed (uvw) # coordinate system - # TODO: Fix key? - orientation_msg = { - "data": { - "camera": { + orientation_msg = self.generate_payload_json( + push_timestamp=int(datetime.utcnow().timestamp()), + device_type=os.environ.get("DEVICE_TYPE", "Collector"), + id_=self.hostname, + deployment_id=os.environ.get( + "DEPLOYMENT_ID", f"Unknown-Location-{self.hostname}" + ), + current_location=os.environ.get("CURRENT_LOCATION", "-90, -180"), + status="Debug", + message_type="Event", + model_version="null", + firmware_version="v0.0.0", + data_payload_type="Orientation", + data_payload=json.dumps( + { "tripod_yaw": self.alpha, "tripod_pitch": self.beta, "tripod_roll": self.gamma, } - } - } + ), + ) self._orientation_callback(None, None, orientation_msg) # Initialize camera pointing @@ -344,7 +372,7 @@ def __init__( # Log configuration parameters logging.info( f"""AxisPtzController initialized with parameters: - + hostname = {hostname} camera_ip = {camera_ip} camera_user = {camera_user} camera_password = {camera_password} @@ -381,7 +409,9 @@ def __init__( """ ) - def decode_payload(self, payload: mqtt.MQTTMessage) -> Dict[Any, Any]: + def decode_payload( + self, msg: Union[mqtt.MQTTMessage, str], data_payload_type: str + ) -> Dict[Any, Any]: """ Decode the payload carried by a message. @@ -393,21 +423,20 @@ def decode_payload(self, payload: mqtt.MQTTMessage) -> Dict[Any, Any]: Returns ------- data : Dict[Any, Any] - The data component of the payload + The data payload of the message payload """ - # TODO: Establish and use message format convention - content = json.loads(str(payload.decode("utf-8"))) - if "data" in content: - data = content["data"] + if type(msg) == mqtt.MQTTMessage: + payload = msg.payload.decode() else: - data = content - return data + payload = msg # type: ignore + data_payload = json.loads(payload)[data_payload_type] + return json.loads(data_payload) def _config_callback( self, _client: Union[mqtt.Client, None], _userdata: Union[Dict[Any, Any], None], - msg: Union[mqtt.MQTTMessage, Dict[Any, Any]], + msg: Union[mqtt.MQTTMessage, str], ) -> None: """ Process configuration message. @@ -426,15 +455,12 @@ def _config_callback( None """ # Assign data attributes allowed to change during operation, - # ignoring config message data without a "camera" key - if type(msg) == mqtt.MQTTMessage: - data = self.decode_payload(msg.payload) - else: - data = msg["data"] + # ignoring config message data without a "axis-ptz-controller" + # key + data = self.decode_payload(msg, "Configuration") if "axis-ptz-controller" not in data: return logging.info(f"Processing config msg data: {data}") - # TODO: Use module specific key? config = data["axis-ptz-controller"] self.lambda_t = config.get("tripod_longitude", self.lambda_t) # [deg] self.varphi_t = config.get("tripod_latitude", self.varphi_t) # [deg] @@ -472,7 +498,7 @@ def _orientation_callback( self, _client: Union[mqtt.Client, None], _userdata: Union[Dict[Any, Any], None], - msg: Union[mqtt.MQTTMessage, Dict[Any, Any]], + msg: Union[mqtt.MQTTMessage, str], ) -> None: """ Process orientation message. @@ -491,15 +517,11 @@ def _orientation_callback( None """ # Assign camera housing rotation angles - if type(msg) == mqtt.MQTTMessage: - data = self.decode_payload(msg.payload) - else: - data = msg["data"] + data = self.decode_payload(msg, "Orientation") logging.info(f"Processing orientation msg data: {data}") - camera = data["camera"] - self.alpha = camera["tripod_yaw"] # [deg] - self.beta = camera["tripod_pitch"] # [deg] - self.gamma = camera["tripod_roll"] # [deg] + self.alpha = data["tripod_yaw"] # [deg] + self.beta = data["tripod_pitch"] # [deg] + self.gamma = data["tripod_roll"] # [deg] # Compute the rotations from the geocentric (XYZ) coordinate # system to the camera housing fixed (uvw) coordinate system @@ -528,7 +550,7 @@ def _object_callback( self, _client: Union[mqtt.Client, None], _userdata: Union[Dict[Any, Any], None], - msg: Union[mqtt.MQTTMessage, Dict[Any, Any]], + msg: Union[mqtt.MQTTMessage, str], ) -> None: """ Process object message. @@ -548,10 +570,7 @@ def _object_callback( """ # Assign identifier, time, position, and velocity of the # object - if type(msg) == mqtt.MQTTMessage: - data = json.loads(self.decode_payload(msg.payload)["Selected Object"]) - else: - data = msg["data"] + data = self.decode_payload(msg, "Selected Object") if not set( [ "object_id", @@ -753,26 +772,39 @@ def _object_callback( # Log camera pointing using MQTT if self.log_to_mqtt: - msg = { - "timestamp": datetime.utcnow().timestamp(), - "data": { - "camera-pointing": { - "timestamp_c": self.timestamp_c, - "rho_o": self.rho_o, - "tau_o": self.tau_o, - "rho_dot_o": self.rho_dot_o, - "tau_dot_o": self.tau_dot_o, - "rho_c": self.rho_c, - "tau_c": self.tau_c, - "rho_dot_c": self.rho_dot_c, - "tau_dot_c": self.tau_dot_c, + logger_msg = self.generate_payload_json( + push_timestamp=int(datetime.utcnow().timestamp()), + device_type=os.environ.get("DEVICE_TYPE", "Collector"), + id_=self.hostname, + deployment_id=os.environ.get( + "DEPLOYMENT_ID", f"Unknown-Location-{self.hostname}" + ), + current_location=os.environ.get("CURRENT_LOCATION", "-90, -180"), + status="Debug", + message_type="Event", + model_version="null", + firmware_version="v0.0.0", + data_payload_type="Logger", + data_payload=json.dumps( + { + "camera-pointing": { + "timestamp_c": self.timestamp_c, + "rho_o": self.rho_o, + "tau_o": self.tau_o, + "rho_dot_o": self.rho_dot_o, + "tau_dot_o": self.tau_dot_o, + "rho_c": self.rho_c, + "tau_c": self.tau_c, + "rho_dot_c": self.rho_dot_c, + "tau_dot_c": self.tau_dot_c, + } } - }, - } - logging.debug(f"Publishing logger msg: {msg}") - self.publish_to_topic(self.logger_topic, json.dumps(msg)) + ), + ) + logging.debug(f"Publishing logger msg: {logger_msg}") + self.publish_to_topic(self.logger_topic, logger_msg) - def _publish_data(self: Any, data: Dict[str, str]) -> bool: + def _send_data(self: Any, data: Dict[str, str]) -> bool: """Leverages edgetech-core functionality to publish a JSON payload to the MQTT broker on the topic specified by type. @@ -911,7 +943,7 @@ def _capture_image(self) -> None: # Publish the image after base64 encoding encoded_image = axis_ptz_utilities.encode_image(image_filepath) - self._publish_data( + self._send_data( { "type": "Encoded Image", "payload": encoded_image, @@ -942,7 +974,7 @@ def _capture_image(self) -> None: logging.debug( f"Publishing metadata: {image_metadata}, for object: {self.object_id}, at: {self.capture_time}" ) - self._publish_data( + self._send_data( { "type": "Image Metadata", "payload": json.dumps(image_metadata), @@ -1045,6 +1077,7 @@ def main(self) -> None: def make_controller() -> AxisPtzController: return AxisPtzController( + hostname=os.environ.get("HOSTNAME", ""), camera_ip=os.environ.get("CAMERA_IP", ""), camera_user=os.environ.get("CAMERA_USER", ""), camera_password=os.environ.get("CAMERA_PASSWORD", ""), diff --git a/axis-ptz-controller/axis_ptz_utilities.py b/axis-ptz-controller/axis_ptz_utilities.py index 7f7b382..527e1ff 100644 --- a/axis-ptz-controller/axis_ptz_utilities.py +++ b/axis-ptz-controller/axis_ptz_utilities.py @@ -1,5 +1,4 @@ import base64 -import coloredlogs import contextlib from datetime import datetime import logging @@ -8,6 +7,7 @@ from pathlib import Path from typing import cast, Generator, Tuple, Union +import coloredlogs import numpy as np import numpy.typing as npt import quaternion diff --git a/axis-ptz-controller/camera_configuration.py b/axis-ptz-controller/camera_configuration.py index 50654bc..281d257 100644 --- a/axis-ptz-controller/camera_configuration.py +++ b/axis-ptz-controller/camera_configuration.py @@ -1,8 +1,8 @@ # mypy: ignore-errors -import coloredlogs import datetime import logging +import coloredlogs import requests from requests.auth import HTTPDigestAuth from sensecam_control import vapix_config diff --git a/axis-ptz-controller/camera_control.py b/axis-ptz-controller/camera_control.py index 9766687..99854ca 100644 --- a/axis-ptz-controller/camera_control.py +++ b/axis-ptz-controller/camera_control.py @@ -1,6 +1,6 @@ -import coloredlogs import logging +import coloredlogs from sensecam_control import vapix_control from typing import Tuple, Union diff --git a/axis-ptz-controller/test_integration.py b/axis-ptz-controller/test_integration.py index 5fb4a0e..cf3934d 100644 --- a/axis-ptz-controller/test_integration.py +++ b/axis-ptz-controller/test_integration.py @@ -6,10 +6,31 @@ import time from typing import Any, Dict +import coloredlogs from matplotlib import pyplot as plt import pandas as pd -import axis_ptz_controller +from axis_ptz_controller import AxisPtzController + +STYLES = { + "critical": {"bold": True, "color": "red"}, + "debug": {"color": "green"}, + "error": {"color": "red"}, + "info": {"color": "white"}, + "notice": {"color": "magenta"}, + "spam": {"color": "green", "faint": True}, + "success": {"bold": True, "color": "green"}, + "verbose": {"color": "blue"}, + "warning": {"color": "yellow"}, +} +coloredlogs.install( + level=logging.INFO, + fmt="%(asctime)s.%(msecs)03d \033[0;90m%(levelname)-8s " + "" + "\033[0;36m%(filename)-18s%(lineno)3d\033[00m " + "%(message)s", + level_styles=STYLES, +) HEARTBEAT_INTERVAL = 10 UPDATE_INTERVAL = 0.01 @@ -24,11 +45,8 @@ JPEG_RESOLUTION = "1920x1080" JPEG_COMPRESSION = 5 -logger = logging.getLogger("ptz-integration") -logger.setLevel(logging.INFO) - -def make_controller(use_mqtt: bool) -> axis_ptz_controller.AxisPtzController: +def make_controller(use_mqtt: bool) -> AxisPtzController: """Construct a controller. Note that if use_mqtt = True then an MQTT broker must be started @@ -43,7 +61,8 @@ def make_controller(use_mqtt: bool) -> axis_ptz_controller.AxisPtzController: ------- None """ - controller = axis_ptz_controller.AxisPtzController( + controller = AxisPtzController( + hostname=os.environ.get("HOSTNAME", ""), camera_ip=os.getenv("CAMERA_IP", ""), camera_user=os.getenv("CAMERA_USER", ""), camera_password=os.getenv("CAMERA_PASSWORD", ""), @@ -73,7 +92,7 @@ def make_controller(use_mqtt: bool) -> axis_ptz_controller.AxisPtzController: return controller -def get_config_msg() -> Dict[Any, Any]: +def get_config_msg(controller: AxisPtzController) -> str: """Populate a config message, reading actual, by private longitude and latitude from the environment. @@ -83,23 +102,35 @@ def get_config_msg() -> Dict[Any, Any]: Returns ------- - msg : Dict[Any, Any] + msg : str The configuration message """ - msg: Dict[Any, Any] = {} - msg["data"] = {} - msg["data"]["axis-ptz-controller"] = {} - msg["data"]["axis-ptz-controller"]["tripod_longitude"] = float( + data: Dict[str, Any] = {} + data["axis-ptz-controller"] = {} + data["axis-ptz-controller"]["tripod_longitude"] = float( os.getenv("TRIPOD_LONGITUDE", "-77.0") ) # [deg] - msg["data"]["axis-ptz-controller"]["tripod_latitude"] = float( + data["axis-ptz-controller"]["tripod_latitude"] = float( os.getenv("TRIPOD_LATITUDE", "38.0") ) # [deg] - msg["data"]["axis-ptz-controller"]["tripod_altitude"] = 86.46 # [m] + data["axis-ptz-controller"]["tripod_altitude"] = 86.46 # [m] + msg = controller.generate_payload_json( + push_timestamp=int(datetime.utcnow().timestamp()), + device_type="TBC", + id_="TBC", + deployment_id="TBC", + current_location="TBC", + status="Debug", + message_type="Event", + model_version="null", + firmware_version="v0.0.0", + data_payload_type="Configuration", + data_payload=json.dumps(data), + ) return msg -def get_orientation_msg() -> Dict[Any, Any]: +def get_orientation_msg(controller: AxisPtzController) -> str: """Populate an orientation message with all 0 deg angles. Parameters @@ -108,15 +139,30 @@ def get_orientation_msg() -> Dict[Any, Any]: Returns ------- - msg : dict + msg : str The orientation message """ - with open("data/orientation_msg_0s.json", "r") as f: - msg = json.load(f) + with open("data/orientation_msg_data_0s.json", "r") as f: + data = json.load(f) + msg = controller.generate_payload_json( + push_timestamp=int(datetime.utcnow().timestamp()), + device_type="TBC", + id_="TBC", + deployment_id="TBC", + current_location="TBC", + status="Debug", + message_type="Event", + model_version="null", + firmware_version="v0.0.0", + data_payload_type="Orientation", + data_payload=json.dumps(data), + ) return msg -def make_object_msg(track: pd.DataFrame, index: int) -> Dict[Any, Any]: +def make_object_msg( + controller: AxisPtzController, track: pd.DataFrame, index: int +) -> str: """Populate a object message with track data at the specified index. @@ -129,11 +175,23 @@ def make_object_msg(track: pd.DataFrame, index: int) -> Dict[Any, Any]: Returns ------- - msg : dict + msg : str The object message """ - msg = {} - msg["data"] = track.iloc[index, :].to_dict() + data = track.iloc[index, :].to_dict() + msg = controller.generate_payload_json( + push_timestamp=int(datetime.utcnow().timestamp()), + device_type="TBC", + id_="TBC", + deployment_id="TBC", + current_location="TBC", + status="Debug", + message_type="Event", + model_version="null", + firmware_version="v0.0.0", + data_payload_type="Selected Object", + data_payload=json.dumps(data), + ) return msg @@ -243,48 +301,33 @@ def main() -> None: args = parser.parse_args() # Read the track data - logger.info(f"Reading track for id: {args.track_id}") + logging.info(f"Reading track for id: {args.track_id}") track = read_track_data(args.track_id) # Make the controller, subscribe to all topics, and publish, or # process, one message to each topic - logger.info("Making the controller, and subscribing to topics") + logging.info("Making the controller, and subscribing to topics") controller = make_controller(args.use_mqtt) controller.add_subscribe_topic(controller.config_topic, controller._config_callback) controller.add_subscribe_topic( controller.orientation_topic, controller._orientation_callback ) controller.add_subscribe_topic(controller.object_topic, controller._object_callback) - config_msg = get_config_msg() - orientation_msg = get_orientation_msg() + config_msg = get_config_msg(controller) + orientation_msg = get_orientation_msg(controller) index = 0 - object_msg = make_object_msg(track, index) + object_msg = make_object_msg(controller, track, index) if controller.use_mqtt: - logger.info(f"Publishing config msg: {config_msg}") - controller.publish_to_topic(controller.config_topic, json.dumps(config_msg)) + logging.info(f"Publishing config msg: {config_msg}") + controller.publish_to_topic(controller.config_topic, config_msg) time.sleep(UPDATE_INTERVAL) - logger.info(f"Publishing orientation msg: {orientation_msg}") - controller.publish_to_topic( - controller.orientation_topic, json.dumps(orientation_msg) - ) + logging.info(f"Publishing orientation msg: {orientation_msg}") + controller.publish_to_topic(controller.orientation_topic, orientation_msg) time.sleep(UPDATE_INTERVAL) - logger.info(f"Publishing object msg: {object_msg}") - payload_json = controller.generate_payload_json( - push_timestamp=int(datetime.utcnow().timestamp()), - device_type="TBC", - id_="TBC", - deployment_id="TBC", - current_location="TBC", - status="Debug", - message_type="Event", - model_version="null", - firmware_version="v0.0.0", - data_payload_type="Selected Object", - data_payload=json.dumps(object_msg["data"]), - ) - controller.publish_to_topic(controller.object_topic, payload_json) + logging.info(f"Publishing object msg: {object_msg}") + controller.publish_to_topic(controller.object_topic, object_msg) time.sleep(UPDATE_INTERVAL) else: @@ -296,7 +339,8 @@ def main() -> None: # Initialize history for plotting history = {} - history["timestamp_c"] = [object_msg["data"]["timestamp"]] + data = controller.decode_payload(object_msg, "Selected Object") + history["timestamp_c"] = [data["timestamp"]] history["rho_o"] = [controller.rho_o] history["tau_o"] = [controller.tau_o] history["rho_dot_o"] = [controller.rho_dot_o] @@ -315,23 +359,10 @@ def main() -> None: # Process each object message when received if timestamp_c >= track["timestamp"][index + 1]: index = track["timestamp"][timestamp_c >= track["timestamp"]].index[-1] - object_msg = make_object_msg(track, index) + object_msg = make_object_msg(controller, track, index) if controller.use_mqtt: - logger.info(f"Publishing object msg: {object_msg}") - payload_json = controller.generate_payload_json( - push_timestamp=int(datetime.utcnow().timestamp()), - device_type="TBC", - id_="TBC", - deployment_id="TBC", - current_location="TBC", - status="Debug", - message_type="Event", - model_version="null", - firmware_version="v0.0.0", - data_payload_type="Selected Object", - data_payload=json.dumps(object_msg["data"]), - ) - controller.publish_to_topic(controller.object_topic, payload_json) + logging.info(f"Publishing object msg: {object_msg}") + controller.publish_to_topic(controller.object_topic, object_msg) time.sleep(UPDATE_INTERVAL) else: diff --git a/axis-ptz-controller/test_messages.py b/axis-ptz-controller/test_messages.py index 65d1ab0..07050da 100644 --- a/axis-ptz-controller/test_messages.py +++ b/axis-ptz-controller/test_messages.py @@ -4,24 +4,43 @@ import logging import os import time -from typing import Any, Dict +from typing import Any, Dict, Union +import coloredlogs import paho.mqtt.client as mqtt import pandas as pd from base_mqtt_pub_sub import BaseMQTTPubSub from test_integration import ( - read_track_data, get_config_msg, get_orientation_msg, + make_controller, make_object_msg, plot_time_series, + read_track_data, ) -UPDATE_INTERVAL = 0.1 +STYLES = { + "critical": {"bold": True, "color": "red"}, + "debug": {"color": "green"}, + "error": {"color": "red"}, + "info": {"color": "white"}, + "notice": {"color": "magenta"}, + "spam": {"color": "green", "faint": True}, + "success": {"bold": True, "color": "green"}, + "verbose": {"color": "blue"}, + "warning": {"color": "yellow"}, +} +coloredlogs.install( + level=logging.INFO, + fmt="%(asctime)s.%(msecs)03d \033[0;90m%(levelname)-8s " + "" + "\033[0;36m%(filename)-18s%(lineno)3d\033[00m " + "%(message)s", + level_styles=STYLES, +) -logger = logging.getLogger("ptz-messages") -logger.setLevel(logging.INFO) +UPDATE_INTERVAL = 0.1 class MessageHandler(BaseMQTTPubSub): @@ -66,7 +85,7 @@ def __init__( self.logger_topic = logger_topic # Connect MQTT client - logger.info("Connecting MQTT client") + logging.info("Connecting MQTT client") self.connect_client() time.sleep(5) self.publish_registration("Message Handler Module Registration") @@ -75,11 +94,11 @@ def __init__( self.camera_pointing_filename = "camera-pointing.csv" self.camera_pointing_file = open(self.camera_pointing_filename, "w") self.camera_pointing_keys = [ - "time_c", - "rho_a", - "tau_a", - "rho_dot_a", - "tau_dot_a", + "timestamp_c", + "rho_o", + "tau_o", + "rho_dot_o", + "tau_dot_o", "rho_c", "tau_c", "rho_dot_c", @@ -87,22 +106,28 @@ def __init__( ] self.camera_pointing_file.write(",".join(self.camera_pointing_keys) + "\n") - def decode_payload(self, payload: mqtt.MQTTMessage) -> Dict[Any, Any]: + def decode_payload( + self, msg: Union[mqtt.MQTTMessage, str], data_payload_type: str + ) -> Dict[Any, Any]: """ Decode the payload carried by a message. Parameters ---------- - payload: Any - A JSON string with {timestamp: ____, data: ____,} + payload: mqtt.MQTTMessage + The MQTT message Returns ------- - data : dict - The data component of the payload + data : Dict[Any, Any] + The data payload of the message payload """ - data = json.loads(str(payload.decode("utf-8")))["data"] - return data + if type(msg) == mqtt.MQTTMessage: + payload = msg.payload.decode() + else: + payload = msg # type: ignore + data_payload = json.loads(payload)[data_payload_type] + return json.loads(data_payload) def _logger_callback( self, _client: mqtt.Client, _userdata: Dict[Any, Any], msg: mqtt.MQTTMessage @@ -123,14 +148,18 @@ def _logger_callback( ------- None """ - data = self.decode_payload(msg.payload) + data = self.decode_payload(msg, "Logger") if "camera-pointing" in data: - p = data["camera-pointing"] + logging.info(data["camera-pointing"]) self.camera_pointing_file.write( - ",".join([str(p[k]) for k in self.camera_pointing_keys]) + "\n" + ",".join( + [str(data["camera-pointing"][k]) for k in self.camera_pointing_keys] + ) + + "\n" ) + self.camera_pointing_file.flush() elif "info" in data: - logger.info(data["info"]["message"]) + logging.info(data["info"]["message"]) def make_handler() -> MessageHandler: @@ -184,54 +213,68 @@ def main() -> None: args = parser.parse_args() # Read the track data - logger.info(f"Reading track for id: {args.track_id}") + logging.info(f"Reading track for id: {args.track_id}") track = read_track_data(args.track_id) # Make the handler, and subscribe to the logger topic - logger.info("Making the handler, and subscribing to topics") + logging.info("Making the handler, and subscribing to topics") handler = make_handler() handler.add_subscribe_topic(handler.logger_topic, handler._logger_callback) - logger_msg = { - "timestamp": str(int(datetime.utcnow().timestamp())), - "data": { - "info": { - "message": "Subscribed to the logger", - } - }, + data = { + "info": { + "message": "Subscribed to the logger", + } } - handler.publish_to_topic(handler.logger_topic, json.dumps(logger_msg)) + logger_msg = handler.generate_payload_json( + push_timestamp=int(datetime.utcnow().timestamp()), + device_type="TBC", + id_="TBC", + deployment_id="TBC", + current_location="TBC", + status="Debug", + message_type="Event", + model_version="null", + firmware_version="v0.0.0", + data_payload_type="Logger", + data_payload=json.dumps(data), + ) + handler.publish_to_topic(handler.logger_topic, logger_msg) # Publish the configuration and orientation message, and the first # object message - config_msg = get_config_msg() - orientation_msg = get_orientation_msg() + controller = make_controller(True) + config_msg = get_config_msg(controller) + orientation_msg = get_orientation_msg(controller) index = 0 - object_msg = make_object_msg(track, index) - logger.info(f"Publishing config msg: {config_msg}") - handler.publish_to_topic(handler.config_topic, json.dumps(config_msg)) + object_msg = make_object_msg(controller, track, index) + logging.info(f"Publishing config msg: {config_msg}") + handler.publish_to_topic(handler.config_topic, config_msg) time.sleep(UPDATE_INTERVAL) - logger.info(f"Publishing orientation msg: {orientation_msg}") - handler.publish_to_topic(handler.orientation_topic, json.dumps(orientation_msg)) + + logging.info(f"Publishing orientation msg: {orientation_msg}") + handler.publish_to_topic(handler.orientation_topic, orientation_msg) time.sleep(UPDATE_INTERVAL) - logger.info(f"Publishing object msg: {object_msg}") - handler.publish_to_topic(handler.object_topic, json.dumps(object_msg)) + + logging.info(f"Publishing object msg: {object_msg}") + handler.publish_to_topic(handler.object_topic, object_msg) time.sleep(UPDATE_INTERVAL) # Loop in camera time dt_c = UPDATE_INTERVAL - time_c = track["latLonTime"][index] + timestamp_c = track["timestamp"][index] while index < track.shape[0] - 1: time.sleep(UPDATE_INTERVAL) - time_c += dt_c + timestamp_c += dt_c # Process each object message when received - if time_c >= track["latLonTime"][index + 1]: - index = track["latLonTime"][time_c >= track["latLonTime"]].index[-1] - object_msg = make_object_msg(track, index) - logger.info(f"Publishing object msg: {object_msg}") - handler.publish_to_topic(handler.object_topic, json.dumps(object_msg)) + if timestamp_c >= track["timestamp"][index + 1]: + index = track["timestamp"][timestamp_c >= track["timestamp"]].index[-1] + object_msg = make_object_msg(controller, track, index) + logging.info(f"Publishing object msg: {object_msg}") + handler.publish_to_topic(handler.object_topic, object_msg) # Read camera pointing file as a dataframe, and plot + time.sleep(5) handler.camera_pointing_file.close() ts = pd.read_csv(handler.camera_pointing_filename) plot_time_series(ts) diff --git a/axis-ptz-controller/test_modules.py b/axis-ptz-controller/test_modules.py index 8693e0a..d29e796 100644 --- a/axis-ptz-controller/test_modules.py +++ b/axis-ptz-controller/test_modules.py @@ -1,3 +1,4 @@ +from datetime import datetime import json import math import os @@ -8,7 +9,8 @@ import numpy.typing as npt import pytest import quaternion -import axis_ptz_controller + +from axis_ptz_controller import AxisPtzController import axis_ptz_utilities @@ -54,9 +56,10 @@ def R_pole() -> float: @pytest.fixture -def controller() -> axis_ptz_controller.AxisPtzController: +def controller() -> AxisPtzController: """Construct a controller.""" - controller = axis_ptz_controller.AxisPtzController( + controller = AxisPtzController( + hostname=os.environ.get("HOSTNAME", ""), camera_ip=os.getenv("CAMERA_IP", ""), camera_user=os.getenv("CAMERA_USER", ""), camera_password=os.getenv("CAMERA_PASSWORD", ""), @@ -87,31 +90,70 @@ def controller() -> axis_ptz_controller.AxisPtzController: @pytest.fixture -def config_msg() -> Dict[Any, Any]: +def config_msg(controller: AxisPtzController) -> str: """Populate a config message.""" - with open("data/config_msg.json", "r") as f: - msg = json.load(f) + with open("data/config_msg_data.json", "r") as f: + data = json.load(f) + msg = controller.generate_payload_json( + push_timestamp=int(datetime.utcnow().timestamp()), + device_type="TBC", + id_="TBC", + deployment_id="TBC", + current_location="TBC", + status="Debug", + message_type="Event", + model_version="null", + firmware_version="v0.0.0", + data_payload_type="Configuration", + data_payload=json.dumps(data), + ) return msg @pytest.fixture -def orientation_msg_0s() -> Dict[Any, Any]: +def orientation_msg_0s(controller: AxisPtzController) -> str: """Populate an orientation message with all 0 deg angles.""" - with open("data/orientation_msg_0s.json", "r") as f: - msg = json.load(f) + with open("data/orientation_msg_data_0s.json", "r") as f: + data = json.load(f) + msg = controller.generate_payload_json( + push_timestamp=int(datetime.utcnow().timestamp()), + device_type="TBC", + id_="TBC", + deployment_id="TBC", + current_location="TBC", + status="Debug", + message_type="Event", + model_version="null", + firmware_version="v0.0.0", + data_payload_type="Orientation", + data_payload=json.dumps(data), + ) return msg @pytest.fixture -def orientation_msg_90s() -> Dict[Any, Any]: +def orientation_msg_90s(controller: AxisPtzController) -> str: """Populate an orientation message with all 90 deg angles.""" - with open("data/orientation_msg_90s.json", "r") as f: - msg = json.load(f) + with open("data/orientation_msg_data_90s.json", "r") as f: + data = json.load(f) + msg = controller.generate_payload_json( + push_timestamp=int(datetime.utcnow().timestamp()), + device_type="TBC", + id_="TBC", + deployment_id="TBC", + current_location="TBC", + status="Debug", + message_type="Event", + model_version="null", + firmware_version="v0.0.0", + data_payload_type="Orientation", + data_payload=json.dumps(data), + ) return msg @pytest.fixture -def object_msg() -> Dict[Any, Any]: +def object_msg(controller: AxisPtzController) -> str: """Populate a object message with velocity along the line of sight, using the calculation noted below. @@ -125,8 +167,21 @@ def object_msg() -> Dict[Any, Any]: E_XYZ_to_ENz, _, _, _ = axis_ptz_utilities.compute_E_XYZ_to_ENz(LAMBDA_O, VARPHI_O) v_ENz_A_o = np.matmul(E_XYZ_to_ENz, v_ENz_T_o) """ - with open("data/object_msg.json", "r") as f: - msg = json.load(f) + with open("data/object_msg_data.json", "r") as f: + data = json.load(f) + msg = controller.generate_payload_json( + push_timestamp=int(datetime.utcnow().timestamp()), + device_type="TBC", + id_="TBC", + deployment_id="TBC", + current_location="TBC", + status="Debug", + message_type="Event", + model_version="null", + firmware_version="v0.0.0", + data_payload_type="Selected Object", + data_payload=json.dumps(data), + ) return msg @@ -135,8 +190,8 @@ class TestAxisPtzController: def test_config_callback( self, - controller: axis_ptz_controller.AxisPtzController, - config_msg: Dict[Any, Any], + controller: AxisPtzController, + config_msg: str, ) -> None: # Align ENz with XYZ _client = None @@ -163,9 +218,9 @@ def test_config_callback( def test_orientation_callback( self, - controller: axis_ptz_controller.AxisPtzController, - config_msg: Dict[Any, Any], - orientation_msg_90s: Dict[Any, Any], + controller: AxisPtzController, + config_msg: str, + orientation_msg_90s: str, ) -> None: # Align ENz with XYZ _client = None @@ -198,26 +253,22 @@ def test_orientation_callback( assert qnorm(controller.q_gamma - q_gamma_exp) < PRECISION assert np.linalg.norm(controller.E_XYZ_to_uvw - E_XYZ_to_uvw_exp) < PRECISION - def test_compute_pan_rate_index( - self, controller: axis_ptz_controller.AxisPtzController - ) -> None: + def test_compute_pan_rate_index(self, controller: AxisPtzController) -> None: assert controller._compute_pan_rate_index(-PAN_RATE_MAX * 2.0) == -100 assert controller._compute_pan_rate_index(0.0) == 0 assert controller._compute_pan_rate_index(PAN_RATE_MAX * 2.0) == +100 - def test_compute_tilt_rate_index( - self, controller: axis_ptz_controller.AxisPtzController - ) -> None: + def test_compute_tilt_rate_index(self, controller: AxisPtzController) -> None: assert controller._compute_tilt_rate_index(-TILT_RATE_MAX * 2.0) == -100 assert controller._compute_tilt_rate_index(0.0) == 0 assert controller._compute_tilt_rate_index(TILT_RATE_MAX * 2.0) == +100 def test_object_callback( self, - controller: axis_ptz_controller.AxisPtzController, - config_msg: Dict[Any, Any], - orientation_msg_0s: Dict[Any, Any], - object_msg: Dict[Any, Any], + controller: AxisPtzController, + config_msg: str, + orientation_msg_0s: str, + object_msg: str, ) -> None: # Align ENz with XYZ _client = None @@ -503,15 +554,14 @@ def test_great_circle_distance( ) assert math.fabs((d_act - d_exp) / d_exp) < PRECISION - # Base64 encode an image from a file - def test_encode_image(self): - + def test_encode_image(self) -> None: image_filepath = Path("data/acc31a_97_1_2358_2023-06-14-15-32-59.jpg") + with open(image_filepath, "rb") as image_file: image = image_file.read() encoded_image = axis_ptz_utilities.encode_image(image_filepath) decoded_image = axis_ptz_utilities.decode_image(encoded_image) - assert(decoded_image == image) + assert decoded_image == image From 8f2865c684681437f0585531eead35e1d6bd5a38 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 31 Jul 2023 11:53:38 -0400 Subject: [PATCH 08/56] Remove unneeded typing import --- axis-ptz-controller/test_modules.py | 1 - 1 file changed, 1 deletion(-) diff --git a/axis-ptz-controller/test_modules.py b/axis-ptz-controller/test_modules.py index d29e796..42b16b2 100644 --- a/axis-ptz-controller/test_modules.py +++ b/axis-ptz-controller/test_modules.py @@ -3,7 +3,6 @@ import math import os from pathlib import Path -from typing import Any, Dict import numpy as np import numpy.typing as npt From 8f761a62e4ccc1adb4fc8153deb88269141da214 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 31 Jul 2023 11:56:36 -0400 Subject: [PATCH 09/56] Remove unneeded type ignores --- axis-ptz-controller/axis_ptz_controller.py | 2 +- axis-ptz-controller/test_messages.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index fa726e1..1b2d879 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -428,7 +428,7 @@ def decode_payload( if type(msg) == mqtt.MQTTMessage: payload = msg.payload.decode() else: - payload = msg # type: ignore + payload = msg data_payload = json.loads(payload)[data_payload_type] return json.loads(data_payload) diff --git a/axis-ptz-controller/test_messages.py b/axis-ptz-controller/test_messages.py index 07050da..0bce97f 100644 --- a/axis-ptz-controller/test_messages.py +++ b/axis-ptz-controller/test_messages.py @@ -125,7 +125,7 @@ def decode_payload( if type(msg) == mqtt.MQTTMessage: payload = msg.payload.decode() else: - payload = msg # type: ignore + payload = msg data_payload = json.loads(payload)[data_payload_type] return json.loads(data_payload) From 4547bb75f73e993f0578739f694912d61af55a80 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 7 Aug 2023 17:46:57 +0000 Subject: [PATCH 10/56] Fix topic names in config and logging --- axis-ptz-controller/axis_ptz_controller.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index b64f36d..d5d667e 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -54,7 +54,7 @@ class AxisPtzController(BaseMQTTPubSub): """Point the camera at an object using a proportional rate controller, and capture images while in track.""" - def __init__( + def __init__( self, hostname: str, camera_ip: str, @@ -298,6 +298,7 @@ def __init__( self.timestamp_c = 0.0 # [s] self.rho_c = 0.0 # [deg] self.tau_c = 0.0 # [deg] + # TODO: Include in init? self.zoom = 2000 # 1 to 9999 [-] self.focus = 60 # 0 to 100 [%] @@ -437,7 +438,8 @@ def _config_callback( self.config_topic = config.get("config_topic", self.config_topic) self.orientation_topic = config.get("orientation_topic", self.orientation_topic) self.object_topic = config.get("object_topic", self.object_topic) - self.capture_topic = config.get("capture_topic", self.capture_topic) + self.encoded_image_topic = config.get("encoded_image_topic", self.encoded_image_topic) + self.image_metadata_topic = config.get("image_metadata_topic", self.image_metadata_topic) self.logger_topic = config.get("logger_topic", self.logger_topic) self.heartbeat_interval = config.get( "heartbeat_interval", self.heartbeat_interval @@ -496,13 +498,15 @@ def _config_callback( def _log_config(self: Any) -> None: """Logs all paramters that can be set on construction.""" config = { + "hostname": self.hostname, "camera_ip": self.camera_ip, "camera_user": self.camera_user, "camera_password": self.camera_password, "config_topic": self.config_topic, "orientation_topic": self.orientation_topic, "object_topic": self.object_topic, - "capture_topic": self.capture_topic, + "encoded_image_topic": self.encoded_image_topic, + "image_metadata_topic": self.image_metadata_topic, "logger_topic": self.logger_topic, "heartbeat_interval": self.heartbeat_interval, "lambda_t": self.lambda_t, From 1acebb96af3d5d715fe0eec3f053ca93b64331a4 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 7 Aug 2023 13:58:48 -0400 Subject: [PATCH 11/56] Fix indent --- axis-ptz-controller/axis_ptz_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index d5d667e..e74b7c9 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -54,7 +54,7 @@ class AxisPtzController(BaseMQTTPubSub): """Point the camera at an object using a proportional rate controller, and capture images while in track.""" - def __init__( + def __init__( self, hostname: str, camera_ip: str, From 4990688239ed53022ea9120a568e9967b75638d8 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 7 Aug 2023 14:03:19 -0400 Subject: [PATCH 12/56] Address code quality issues --- axis-ptz-controller/axis_ptz_controller.py | 10 +++++++--- axis-ptz-controller/camera_control.py | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index e74b7c9..c7eb727 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -1,4 +1,4 @@ -"""Defines the AxisPtzController child class of BaseMQTTPubSub, and a +y"""Defines the AxisPtzController child class of BaseMQTTPubSub, and a method for making AxisPtzController instances. Instatiates an AxisPtzController, and executes its main() method when run as a module. @@ -438,8 +438,12 @@ def _config_callback( self.config_topic = config.get("config_topic", self.config_topic) self.orientation_topic = config.get("orientation_topic", self.orientation_topic) self.object_topic = config.get("object_topic", self.object_topic) - self.encoded_image_topic = config.get("encoded_image_topic", self.encoded_image_topic) - self.image_metadata_topic = config.get("image_metadata_topic", self.image_metadata_topic) + self.encoded_image_topic = config.get( + "encoded_image_topic", self.encoded_image_topic + ) + self.image_metadata_topic = config.get( + "image_metadata_topic", self.image_metadata_topic + ) self.logger_topic = config.get("logger_topic", self.logger_topic) self.heartbeat_interval = config.get( "heartbeat_interval", self.heartbeat_interval diff --git a/axis-ptz-controller/camera_control.py b/axis-ptz-controller/camera_control.py index 435b778..c80c566 100644 --- a/axis-ptz-controller/camera_control.py +++ b/axis-ptz-controller/camera_control.py @@ -73,6 +73,7 @@ def get_ptz(self) -> Tuple[float, float, float, float]: zoom = float(resp.text.split()[2].split("=")[1]) focus = float(resp.text.split()[3].split("=")[1]) ptz_list = (pan, tilt, zoom, focus) + logging.debug(f"PTZ: {ptz_list}") return ptz_list From c5eb6af2745fec390ece477841fcd899f63d7f59 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 7 Aug 2023 14:04:01 -0400 Subject: [PATCH 13/56] Remove typo --- axis-ptz-controller/axis_ptz_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index c7eb727..04ca205 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -1,4 +1,4 @@ -y"""Defines the AxisPtzController child class of BaseMQTTPubSub, and a +"""Defines the AxisPtzController child class of BaseMQTTPubSub, and a method for making AxisPtzController instances. Instatiates an AxisPtzController, and executes its main() method when run as a module. From a077326cdc002f91ac97d0ad36376307be2a5001 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 7 Aug 2023 19:29:43 +0000 Subject: [PATCH 14/56] Add documentation --- axis-ptz-controller/axis_ptz_controller.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 04ca205..136fcd6 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -389,6 +389,8 @@ def decode_payload( ---------- payload: mqtt.MQTTMessage The MQTT message + data_payload_type: str + The data payload type Returns ------- From 36fc906b6b906392fcfadf0f28ab0f41217181b4 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Tue, 8 Aug 2023 15:07:53 +0000 Subject: [PATCH 15/56] Use common environment variable names --- axis-ptz-controller.env | 8 +-- axis-ptz-controller/axis_ptz_controller.py | 82 +++++++++++----------- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/axis-ptz-controller.env b/axis-ptz-controller.env index 17c7c35..9a9d69d 100644 --- a/axis-ptz-controller.env +++ b/axis-ptz-controller.env @@ -4,10 +4,10 @@ CAMERA_IP=192.168.1.220 CAMERA_USER=root CAMERA_PASSWORD=iqtpass1 CONFIG_TOPIC="skyscan/config/json" -ORIENTATION_TOPIC="skyscan/calibration/json" -OBJECT_TOPIC=/Multimodal/${HOSTNAME}/selection/edgetech-axis-ptz-controller/JSON -ENCODED_IMAGE_TOPIC="skyscan/encoded-image/json" -IMAGE_METADATA_TOPIC="skyscan/image-metadata/json" +ORIENTATION_JSON_TOPIC="skyscan/calibration/json" +OBJECT_JSON_TOPIC=/Multimodal/${HOSTNAME}/selection/edgetech-axis-ptz-controller/JSON +IMAGE_BYTESTRING_TOPIC="skyscan/encoded-image/json" +IMAGE_JSON_TOPIC="skyscan/image-metadata/json" LOGGER_TOPIC="skyscan/logger/json" HEARTBEAT_INTERVAL=10 TRIPOD_LATITUDE=38.89294 diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 136fcd6..2299db8 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -61,16 +61,16 @@ def __init__( camera_user: str, camera_password: str, config_topic: str, - orientation_topic: str, - object_topic: str, - encoded_image_topic: str, - image_metadata_topic: str, + orientation_json_topic: str, + object_json_topic: str, + image_bytestring_topic: str, + image_json_topic: str, logger_topic: str, heartbeat_interval: int, lambda_t: float = 0.0, varphi_t: float = 0.0, h_t: float = 0.0, - update_interval: float = 0.1, + loop_interval: float = 0.1, capture_interval: int = 2, capture_dir: str = ".", lead_time: float = 0.5, @@ -107,13 +107,13 @@ def __init__( Camera user password config_topic: str MQTT topic for subscribing to configuration messages - orientation_topic: str + orientation_json_topic: str MQTT topic for subscribing to orientation messages - object_topic: str + object_json_topic: str MQTT topic for subscribing to object messages - encoded_image_topic: str + image_bytestring_topic: str MQTT topic for publising images in base64 encoding - image_metadata_topic: str + image_json_topic: str MQTT topic for publising image metadata logger_topic: str MQTT topic for publishing logger messages @@ -125,7 +125,7 @@ def __init__( Tripod geodetic latitude [deg] h_t: float = 0.0, Tripod geodetic altitude [deg] - update_interval: float + loop_interval: float Interval at which pointing of the camera is computed [s] capture_interval: int Interval at which the camera image is captured [s] @@ -181,16 +181,16 @@ def __init__( self.camera_user = camera_user self.camera_password = camera_password self.config_topic = config_topic - self.orientation_topic = orientation_topic - self.object_topic = object_topic - self.encoded_image_topic = encoded_image_topic - self.image_metadata_topic = image_metadata_topic + self.orientation_json_topic = orientation_json_topic + self.object_json_topic = object_json_topic + self.image_bytestring_topic = image_bytestring_topic + self.image_json_topic = image_json_topic self.logger_topic = logger_topic self.heartbeat_interval = heartbeat_interval self.lambda_t = lambda_t self.varphi_t = varphi_t self.h_t = h_t - self.update_interval = update_interval + self.loop_interval = loop_interval self.capture_interval = capture_interval self.capture_dir = capture_dir self.lead_time = lead_time @@ -438,13 +438,13 @@ def _config_callback( self.camera_user = config.get("camera_user", self.camera_user) self.camera_password = config.get("camera_password", self.camera_password) self.config_topic = config.get("config_topic", self.config_topic) - self.orientation_topic = config.get("orientation_topic", self.orientation_topic) - self.object_topic = config.get("object_topic", self.object_topic) - self.encoded_image_topic = config.get( - "encoded_image_topic", self.encoded_image_topic + self.orientation_json_topic = config.get("orientation_json_topic", self.orientation_json_topic) + self.object_json_topic = config.get("object_json_topic", self.object_json_topic) + self.image_bytestring_topic = config.get( + "image_bytestring_topic", self.image_bytestring_topic ) - self.image_metadata_topic = config.get( - "image_metadata_topic", self.image_metadata_topic + self.image_json_topic = config.get( + "image_json_topic", self.image_json_topic ) self.logger_topic = config.get("logger_topic", self.logger_topic) self.heartbeat_interval = config.get( @@ -453,8 +453,8 @@ def _config_callback( self.lambda_t = config.get("tripod_longitude", self.lambda_t) # [deg] self.varphi_t = config.get("tripod_latitude", self.varphi_t) # [deg] self.h_t = config.get("tripod_altitude", self.h_t) # [m] - self.update_interval = config.get( - "update_interval", self.update_interval + self.loop_interval = config.get( + "loop_interval", self.loop_interval ) # [s] self.capture_interval = config.get( "capture_interval", self.capture_interval @@ -509,16 +509,16 @@ def _log_config(self: Any) -> None: "camera_user": self.camera_user, "camera_password": self.camera_password, "config_topic": self.config_topic, - "orientation_topic": self.orientation_topic, - "object_topic": self.object_topic, - "encoded_image_topic": self.encoded_image_topic, - "image_metadata_topic": self.image_metadata_topic, + "orientation_json_topic": self.orientation_json_topic, + "object_json_topic": self.object_json_topic, + "image_bytestring_topic": self.image_bytestring_topic, + "image_json_topic": self.image_json_topic, "logger_topic": self.logger_topic, "heartbeat_interval": self.heartbeat_interval, "lambda_t": self.lambda_t, "varphi_t": self.varphi_t, "h_t": self.h_t, - "update_interval": self.update_interval, + "loop_interval": self.loop_interval, "capture_interval": self.capture_interval, "capture_dir": self.capture_dir, "lead_time": self.lead_time, @@ -887,10 +887,10 @@ def _send_data(self: Any, data: Dict[str, str]) -> bool: # Publish payload to the topic selected by type if data["type"] == "Encoded Image": - topic = self.encoded_image_topic + topic = self.image_bytestring_topic elif data["type"] == "Image Metadata": - topic = self.image_metadata_topic + topic = self.image_json_topic success = self.publish_to_topic(topic, payload) if success: @@ -1046,9 +1046,9 @@ def _update_pointing(self) -> None: ------- None """ - self.timestamp_c += self.update_interval - self.rho_c += self.rho_dot_c * self.update_interval - self.tau_c += self.tau_dot_c * self.update_interval + self.timestamp_c += self.loop_interval + self.rho_c += self.rho_dot_c * self.loop_interval + self.tau_c += self.tau_dot_c * self.loop_interval def _exit_handler(self, signum: int, frame: Optional[FrameType]) -> None: """Exit the controller gracefully by stopping continuous pan @@ -1086,8 +1086,8 @@ def main(self) -> None: # Subscribe to required topics self.add_subscribe_topic(self.config_topic, self._config_callback) - self.add_subscribe_topic(self.orientation_topic, self._orientation_callback) - self.add_subscribe_topic(self.object_topic, self._object_callback) + self.add_subscribe_topic(self.orientation_json_topic, self._orientation_callback) + self.add_subscribe_topic(self.object_json_topic, self._object_callback) if self.use_camera: # Schedule image capture @@ -1103,7 +1103,7 @@ def main(self) -> None: schedule.run_pending() # Update camera pointing - sleep(self.update_interval) + sleep(self.loop_interval) if not self.use_camera: self._update_pointing() @@ -1145,16 +1145,16 @@ def make_controller() -> AxisPtzController: camera_password=os.environ.get("CAMERA_PASSWORD", ""), mqtt_ip=os.environ.get("MQTT_IP"), config_topic=os.environ.get("CONFIG_TOPIC", ""), - orientation_topic=os.environ.get("ORIENTATION_TOPIC", ""), - object_topic=os.environ.get("OBJECT_TOPIC", ""), - encoded_image_topic=os.environ.get("ENCODED_IMAGE_TOPIC", ""), - image_metadata_topic=os.environ.get("IMAGE_METADATA_TOPIC", ""), + orientation_json_topic=os.environ.get("ORIENTATION_JSON_TOPIC", ""), + object_json_topic=os.environ.get("OBJECT_JSON_TOPIC", ""), + image_bytestring_topic=os.environ.get("IMAGE_BYTESTRING_TOPIC", ""), + image_json_topic=os.environ.get("IMAGE_JSON_TOPIC", ""), logger_topic=os.environ.get("LOGGER_TOPIC", ""), heartbeat_interval=int(os.environ.get("HEARTBEAT_INTERVAL", 10)), lambda_t=float(os.environ.get("TRIPOD_LONGITUDE", 0.0)), varphi_t=float(os.environ.get("TRIPOD_LATITUDE", 0.0)), h_t=float(os.environ.get("TRIPOD_ALTITUDE", 0.0)), - update_interval=float(os.environ.get("UPDATE_INTERVAL", 0.1)), + loop_interval=float(os.environ.get("UPDATE_INTERVAL", 0.1)), capture_interval=int(os.environ.get("CAPTURE_INTERVAL", 2)), capture_dir=os.environ.get("CAPTURE_DIR", "."), lead_time=float(os.environ.get("LEAD_TIME", 0.5)), From c00b09fe5ca0a9953403318dcdca9ca1c5c862da Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Tue, 8 Aug 2023 15:22:47 +0000 Subject: [PATCH 16/56] Fix code quality issues --- axis-ptz-controller/axis_ptz_controller.py | 16 +++++----- axis-ptz-controller/test_integration.py | 34 ++++++++++++---------- axis-ptz-controller/test_messages.py | 34 +++++++++++----------- axis-ptz-controller/test_modules.py | 12 ++++---- 4 files changed, 49 insertions(+), 47 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 2299db8..85666a5 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -438,14 +438,14 @@ def _config_callback( self.camera_user = config.get("camera_user", self.camera_user) self.camera_password = config.get("camera_password", self.camera_password) self.config_topic = config.get("config_topic", self.config_topic) - self.orientation_json_topic = config.get("orientation_json_topic", self.orientation_json_topic) + self.orientation_json_topic = config.get( + "orientation_json_topic", self.orientation_json_topic + ) self.object_json_topic = config.get("object_json_topic", self.object_json_topic) self.image_bytestring_topic = config.get( "image_bytestring_topic", self.image_bytestring_topic ) - self.image_json_topic = config.get( - "image_json_topic", self.image_json_topic - ) + self.image_json_topic = config.get("image_json_topic", self.image_json_topic) self.logger_topic = config.get("logger_topic", self.logger_topic) self.heartbeat_interval = config.get( "heartbeat_interval", self.heartbeat_interval @@ -453,9 +453,7 @@ def _config_callback( self.lambda_t = config.get("tripod_longitude", self.lambda_t) # [deg] self.varphi_t = config.get("tripod_latitude", self.varphi_t) # [deg] self.h_t = config.get("tripod_altitude", self.h_t) # [m] - self.loop_interval = config.get( - "loop_interval", self.loop_interval - ) # [s] + self.loop_interval = config.get("loop_interval", self.loop_interval) # [s] self.capture_interval = config.get( "capture_interval", self.capture_interval ) # [s] @@ -1086,7 +1084,9 @@ def main(self) -> None: # Subscribe to required topics self.add_subscribe_topic(self.config_topic, self._config_callback) - self.add_subscribe_topic(self.orientation_json_topic, self._orientation_callback) + self.add_subscribe_topic( + self.orientation_json_topic, self._orientation_callback + ) self.add_subscribe_topic(self.object_json_topic, self._object_callback) if self.use_camera: diff --git a/axis-ptz-controller/test_integration.py b/axis-ptz-controller/test_integration.py index cf3934d..9f72dda 100644 --- a/axis-ptz-controller/test_integration.py +++ b/axis-ptz-controller/test_integration.py @@ -33,7 +33,7 @@ ) HEARTBEAT_INTERVAL = 10 -UPDATE_INTERVAL = 0.01 +LOOP_INTERVAL = 0.01 CAPTURE_INTERVAL = 2 LEAD_TIME = 0.0 PAN_GAIN = 0.1 @@ -68,13 +68,13 @@ def make_controller(use_mqtt: bool) -> AxisPtzController: camera_password=os.getenv("CAMERA_PASSWORD", ""), mqtt_ip=os.getenv("MQTT_IP", ""), config_topic=os.getenv("CONFIG_TOPIC", ""), - orientation_topic=os.getenv("ORIENTATION_TOPIC", ""), - object_topic=os.getenv("OBJECT_TOPIC", ""), - encoded_image_topic=os.getenv("ENCODED_IMAGE_TOPIC", ""), - image_metadata_topic=os.getenv("IMAGE_METADATA_TOPIC", ""), + orientation_json_topic=os.getenv("ORIENTATION_JSON_TOPIC", ""), + object_json_topic=os.getenv("OBJECT_JSON_TOPIC", ""), + image_bytestring_topic=os.getenv("IMAGE_BYTESTRING_TOPIC", ""), + image_json_topic=os.getenv("IMAGE_JSON_TOPIC", ""), logger_topic=os.getenv("LOGGER_TOPIC", ""), heartbeat_interval=HEARTBEAT_INTERVAL, - update_interval=UPDATE_INTERVAL, + loop_interval=LOOP_INTERVAL, capture_interval=CAPTURE_INTERVAL, lead_time=LEAD_TIME, pan_gain=PAN_GAIN, @@ -310,9 +310,11 @@ def main() -> None: controller = make_controller(args.use_mqtt) controller.add_subscribe_topic(controller.config_topic, controller._config_callback) controller.add_subscribe_topic( - controller.orientation_topic, controller._orientation_callback + controller.orientation_json_topic, controller._orientation_callback + ) + controller.add_subscribe_topic( + controller.object_json_topic, controller._object_callback ) - controller.add_subscribe_topic(controller.object_topic, controller._object_callback) config_msg = get_config_msg(controller) orientation_msg = get_orientation_msg(controller) index = 0 @@ -320,15 +322,15 @@ def main() -> None: if controller.use_mqtt: logging.info(f"Publishing config msg: {config_msg}") controller.publish_to_topic(controller.config_topic, config_msg) - time.sleep(UPDATE_INTERVAL) + time.sleep(LOOP_INTERVAL) logging.info(f"Publishing orientation msg: {orientation_msg}") - controller.publish_to_topic(controller.orientation_topic, orientation_msg) - time.sleep(UPDATE_INTERVAL) + controller.publish_to_topic(controller.orientation_json_topic, orientation_msg) + time.sleep(LOOP_INTERVAL) logging.info(f"Publishing object msg: {object_msg}") - controller.publish_to_topic(controller.object_topic, object_msg) - time.sleep(UPDATE_INTERVAL) + controller.publish_to_topic(controller.object_json_topic, object_msg) + time.sleep(LOOP_INTERVAL) else: _client = None @@ -351,7 +353,7 @@ def main() -> None: history["tau_dot_c"] = [controller.tau_dot_c] # Loop in camera time - dt_c = controller.update_interval + dt_c = controller.loop_interval timestamp_c = history["timestamp_c"][0] while index < track.shape[0] - 1: timestamp_c += dt_c @@ -362,8 +364,8 @@ def main() -> None: object_msg = make_object_msg(controller, track, index) if controller.use_mqtt: logging.info(f"Publishing object msg: {object_msg}") - controller.publish_to_topic(controller.object_topic, object_msg) - time.sleep(UPDATE_INTERVAL) + controller.publish_to_topic(controller.object_json_topic, object_msg) + time.sleep(LOOP_INTERVAL) else: controller._object_callback(_client, _userdata, object_msg) diff --git a/axis-ptz-controller/test_messages.py b/axis-ptz-controller/test_messages.py index 0bce97f..bd0ff38 100644 --- a/axis-ptz-controller/test_messages.py +++ b/axis-ptz-controller/test_messages.py @@ -40,7 +40,7 @@ level_styles=STYLES, ) -UPDATE_INTERVAL = 0.1 +LOOP_INTERVAL = 0.1 class MessageHandler(BaseMQTTPubSub): @@ -49,8 +49,8 @@ class MessageHandler(BaseMQTTPubSub): def __init__( self, config_topic: str, - orientation_topic: str, - object_topic: str, + orientation_json_topic: str, + object_json_topic: str, logger_topic: str, **kwargs: Any, ): @@ -63,10 +63,10 @@ def __init__( config_topic: str MQTT topic for publishing or subscribing to configuration messages - orientation_topic: str + orientation_json_topic: str MQTT topic for publishing or subscribing to orientation messages - object_topic: str + object_json_topic: str MQTT topic for publishing or subscribing to object messages logger_topic: str @@ -80,8 +80,8 @@ def __init__( # Parent class handles kwargs, including MQTT IP super().__init__(**kwargs) self.config_topic = config_topic - self.orientation_topic = orientation_topic - self.object_topic = object_topic + self.orientation_json_topic = orientation_json_topic + self.object_json_topic = object_json_topic self.logger_topic = logger_topic # Connect MQTT client @@ -179,8 +179,8 @@ def make_handler() -> MessageHandler: handler = MessageHandler( mqtt_ip=os.getenv("MQTT_IP", ""), config_topic=os.getenv("CONFIG_TOPIC", ""), - orientation_topic=os.getenv("ORIENTATION_TOPIC", ""), - object_topic=os.getenv("OBJECT_TOPIC", ""), + orientation_json_topic=os.getenv("ORIENTATION_JSON_TOPIC", ""), + object_json_topic=os.getenv("OBJECT_JSON_TOPIC", ""), logger_topic=os.getenv("LOGGER_TOPIC", ""), ) return handler @@ -249,21 +249,21 @@ def main() -> None: object_msg = make_object_msg(controller, track, index) logging.info(f"Publishing config msg: {config_msg}") handler.publish_to_topic(handler.config_topic, config_msg) - time.sleep(UPDATE_INTERVAL) + time.sleep(LOOP_INTERVAL) logging.info(f"Publishing orientation msg: {orientation_msg}") - handler.publish_to_topic(handler.orientation_topic, orientation_msg) - time.sleep(UPDATE_INTERVAL) + handler.publish_to_topic(handler.orientation_json_topic, orientation_msg) + time.sleep(LOOP_INTERVAL) logging.info(f"Publishing object msg: {object_msg}") - handler.publish_to_topic(handler.object_topic, object_msg) - time.sleep(UPDATE_INTERVAL) + handler.publish_to_topic(handler.object_json_topic, object_msg) + time.sleep(LOOP_INTERVAL) # Loop in camera time - dt_c = UPDATE_INTERVAL + dt_c = LOOP_INTERVAL timestamp_c = track["timestamp"][index] while index < track.shape[0] - 1: - time.sleep(UPDATE_INTERVAL) + time.sleep(LOOP_INTERVAL) timestamp_c += dt_c # Process each object message when received @@ -271,7 +271,7 @@ def main() -> None: index = track["timestamp"][timestamp_c >= track["timestamp"]].index[-1] object_msg = make_object_msg(controller, track, index) logging.info(f"Publishing object msg: {object_msg}") - handler.publish_to_topic(handler.object_topic, object_msg) + handler.publish_to_topic(handler.object_json_topic, object_msg) # Read camera pointing file as a dataframe, and plot time.sleep(5) diff --git a/axis-ptz-controller/test_modules.py b/axis-ptz-controller/test_modules.py index 42b16b2..0fb22f6 100644 --- a/axis-ptz-controller/test_modules.py +++ b/axis-ptz-controller/test_modules.py @@ -29,7 +29,7 @@ AIR_SPEED = 100.0 # [m/s] HEARTBEAT_INTERVAL = 10 -UPDATE_INTERVAL = 0.10 +LOOP_INTERVAL = 0.10 CAPTURE_INTERVAL = 2 LEAD_TIME = 0.0 PAN_GAIN = 0.2 @@ -64,13 +64,13 @@ def controller() -> AxisPtzController: camera_password=os.getenv("CAMERA_PASSWORD", ""), mqtt_ip=os.getenv("MQTT_IP", ""), config_topic=os.getenv("CONFIG_TOPIC", ""), - orientation_topic=os.getenv("ORIENTATION_TOPIC", ""), - object_topic=os.getenv("OBJECT_TOPIC", ""), - encoded_image_topic=os.getenv("ENCODED_IMAGE_TOPIC", ""), - image_metadata_topic=os.getenv("IMAGE_METADATA_TOPIC", ""), + orientation_json_topic=os.getenv("ORIENTATION_JSON_TOPIC", ""), + object_json_topic=os.getenv("OBJECT_JSON_TOPIC", ""), + image_bytestring_topic=os.getenv("IMAGE_BYTESTRING_TOPIC", ""), + image_json_topic=os.getenv("IMAGE_JSON_TOPIC", ""), logger_topic=os.getenv("LOGGER_TOPIC", ""), heartbeat_interval=HEARTBEAT_INTERVAL, - update_interval=UPDATE_INTERVAL, + loop_interval=LOOP_INTERVAL, capture_interval=CAPTURE_INTERVAL, lead_time=LEAD_TIME, pan_gain=PAN_GAIN, From cc804e9cf61aef3aed0c6cfe957d46d34122a268 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Tue, 8 Aug 2023 15:44:09 +0000 Subject: [PATCH 17/56] Update config and logger topics --- axis-ptz-controller.env | 4 ++-- axis-ptz-controller/axis_ptz_controller.py | 28 +++++++++++----------- axis-ptz-controller/test_integration.py | 10 ++++---- axis-ptz-controller/test_messages.py | 22 ++++++++--------- axis-ptz-controller/test_modules.py | 4 ++-- 5 files changed, 35 insertions(+), 33 deletions(-) diff --git a/axis-ptz-controller.env b/axis-ptz-controller.env index 9a9d69d..c6977f9 100644 --- a/axis-ptz-controller.env +++ b/axis-ptz-controller.env @@ -3,12 +3,12 @@ MQTT_IP=mqtt CAMERA_IP=192.168.1.220 CAMERA_USER=root CAMERA_PASSWORD=iqtpass1 -CONFIG_TOPIC="skyscan/config/json" +CONFIG_JSON_TOPIC="skyscan/config/json" ORIENTATION_JSON_TOPIC="skyscan/calibration/json" OBJECT_JSON_TOPIC=/Multimodal/${HOSTNAME}/selection/edgetech-axis-ptz-controller/JSON IMAGE_BYTESTRING_TOPIC="skyscan/encoded-image/json" IMAGE_JSON_TOPIC="skyscan/image-metadata/json" -LOGGER_TOPIC="skyscan/logger/json" +LOGGER_JSON_TOPIC="skyscan/logger/json" HEARTBEAT_INTERVAL=10 TRIPOD_LATITUDE=38.89294 TRIPOD_LONGITUDE=-77.08529 diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 85666a5..c158546 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -60,12 +60,12 @@ def __init__( camera_ip: str, camera_user: str, camera_password: str, - config_topic: str, + config_json_topic: str, orientation_json_topic: str, object_json_topic: str, image_bytestring_topic: str, image_json_topic: str, - logger_topic: str, + logger_json_topic: str, heartbeat_interval: int, lambda_t: float = 0.0, varphi_t: float = 0.0, @@ -105,7 +105,7 @@ def __init__( Camera user name camera_password: str Camera user password - config_topic: str + config_json_topic: str MQTT topic for subscribing to configuration messages orientation_json_topic: str MQTT topic for subscribing to orientation messages @@ -115,7 +115,7 @@ def __init__( MQTT topic for publising images in base64 encoding image_json_topic: str MQTT topic for publising image metadata - logger_topic: str + logger_json_topic: str MQTT topic for publishing logger messages heartbeat_interval: int Interval at which heartbeat message is to be published [s] @@ -180,12 +180,12 @@ def __init__( self.camera_ip = camera_ip self.camera_user = camera_user self.camera_password = camera_password - self.config_topic = config_topic + self.config_json_topic = config_json_topic self.orientation_json_topic = orientation_json_topic self.object_json_topic = object_json_topic self.image_bytestring_topic = image_bytestring_topic self.image_json_topic = image_json_topic - self.logger_topic = logger_topic + self.logger_json_topic = logger_json_topic self.heartbeat_interval = heartbeat_interval self.lambda_t = lambda_t self.varphi_t = varphi_t @@ -437,7 +437,7 @@ def _config_callback( self.camera_ip = config.get("camera_ip", self.camera_ip) self.camera_user = config.get("camera_user", self.camera_user) self.camera_password = config.get("camera_password", self.camera_password) - self.config_topic = config.get("config_topic", self.config_topic) + self.config_json_topic = config.get("config_json_topic", self.config_json_topic) self.orientation_json_topic = config.get( "orientation_json_topic", self.orientation_json_topic ) @@ -446,7 +446,7 @@ def _config_callback( "image_bytestring_topic", self.image_bytestring_topic ) self.image_json_topic = config.get("image_json_topic", self.image_json_topic) - self.logger_topic = config.get("logger_topic", self.logger_topic) + self.logger_json_topic = config.get("logger_json_topic", self.logger_json_topic) self.heartbeat_interval = config.get( "heartbeat_interval", self.heartbeat_interval ) @@ -506,12 +506,12 @@ def _log_config(self: Any) -> None: "camera_ip": self.camera_ip, "camera_user": self.camera_user, "camera_password": self.camera_password, - "config_topic": self.config_topic, + "config_json_topic": self.config_json_topic, "orientation_json_topic": self.orientation_json_topic, "object_json_topic": self.object_json_topic, "image_bytestring_topic": self.image_bytestring_topic, "image_json_topic": self.image_json_topic, - "logger_topic": self.logger_topic, + "logger_json_topic": self.logger_json_topic, "heartbeat_interval": self.heartbeat_interval, "lambda_t": self.lambda_t, "varphi_t": self.varphi_t, @@ -850,7 +850,7 @@ def _object_callback( ), ) logging.debug(f"Publishing logger msg: {logger_msg}") - self.publish_to_topic(self.logger_topic, logger_msg) + self.publish_to_topic(self.logger_json_topic, logger_msg) def _send_data(self: Any, data: Dict[str, str]) -> bool: """Leverages edgetech-core functionality to publish a JSON @@ -1083,7 +1083,7 @@ def main(self) -> None: ) # Subscribe to required topics - self.add_subscribe_topic(self.config_topic, self._config_callback) + self.add_subscribe_topic(self.config_json_topic, self._config_callback) self.add_subscribe_topic( self.orientation_json_topic, self._orientation_callback ) @@ -1144,12 +1144,12 @@ def make_controller() -> AxisPtzController: camera_user=os.environ.get("CAMERA_USER", ""), camera_password=os.environ.get("CAMERA_PASSWORD", ""), mqtt_ip=os.environ.get("MQTT_IP"), - config_topic=os.environ.get("CONFIG_TOPIC", ""), + config_json_topic=os.environ.get("CONFIG_JSON_TOPIC", ""), orientation_json_topic=os.environ.get("ORIENTATION_JSON_TOPIC", ""), object_json_topic=os.environ.get("OBJECT_JSON_TOPIC", ""), image_bytestring_topic=os.environ.get("IMAGE_BYTESTRING_TOPIC", ""), image_json_topic=os.environ.get("IMAGE_JSON_TOPIC", ""), - logger_topic=os.environ.get("LOGGER_TOPIC", ""), + logger_json_topic=os.environ.get("LOGGER_JSON_TOPIC", ""), heartbeat_interval=int(os.environ.get("HEARTBEAT_INTERVAL", 10)), lambda_t=float(os.environ.get("TRIPOD_LONGITUDE", 0.0)), varphi_t=float(os.environ.get("TRIPOD_LATITUDE", 0.0)), diff --git a/axis-ptz-controller/test_integration.py b/axis-ptz-controller/test_integration.py index 9f72dda..2f5dbbb 100644 --- a/axis-ptz-controller/test_integration.py +++ b/axis-ptz-controller/test_integration.py @@ -67,12 +67,12 @@ def make_controller(use_mqtt: bool) -> AxisPtzController: camera_user=os.getenv("CAMERA_USER", ""), camera_password=os.getenv("CAMERA_PASSWORD", ""), mqtt_ip=os.getenv("MQTT_IP", ""), - config_topic=os.getenv("CONFIG_TOPIC", ""), + config_json_topic=os.getenv("CONFIG_JSON_TOPIC", ""), orientation_json_topic=os.getenv("ORIENTATION_JSON_TOPIC", ""), object_json_topic=os.getenv("OBJECT_JSON_TOPIC", ""), image_bytestring_topic=os.getenv("IMAGE_BYTESTRING_TOPIC", ""), image_json_topic=os.getenv("IMAGE_JSON_TOPIC", ""), - logger_topic=os.getenv("LOGGER_TOPIC", ""), + logger_json_topic=os.getenv("LOGGER_JSON_TOPIC", ""), heartbeat_interval=HEARTBEAT_INTERVAL, loop_interval=LOOP_INTERVAL, capture_interval=CAPTURE_INTERVAL, @@ -308,7 +308,9 @@ def main() -> None: # process, one message to each topic logging.info("Making the controller, and subscribing to topics") controller = make_controller(args.use_mqtt) - controller.add_subscribe_topic(controller.config_topic, controller._config_callback) + controller.add_subscribe_topic( + controller.config_json_topic, controller._config_callback + ) controller.add_subscribe_topic( controller.orientation_json_topic, controller._orientation_callback ) @@ -321,7 +323,7 @@ def main() -> None: object_msg = make_object_msg(controller, track, index) if controller.use_mqtt: logging.info(f"Publishing config msg: {config_msg}") - controller.publish_to_topic(controller.config_topic, config_msg) + controller.publish_to_topic(controller.config_json_topic, config_msg) time.sleep(LOOP_INTERVAL) logging.info(f"Publishing orientation msg: {orientation_msg}") diff --git a/axis-ptz-controller/test_messages.py b/axis-ptz-controller/test_messages.py index bd0ff38..3c8feb6 100644 --- a/axis-ptz-controller/test_messages.py +++ b/axis-ptz-controller/test_messages.py @@ -48,10 +48,10 @@ class MessageHandler(BaseMQTTPubSub): def __init__( self, - config_topic: str, + config_json_topic: str, orientation_json_topic: str, object_json_topic: str, - logger_topic: str, + logger_json_topic: str, **kwargs: Any, ): """Initialize a MessageHandler by subscribing to all required @@ -60,7 +60,7 @@ def __init__( Parameters ---------- - config_topic: str + config_json_topic: str MQTT topic for publishing or subscribing to configuration messages orientation_json_topic: str @@ -69,7 +69,7 @@ def __init__( object_json_topic: str MQTT topic for publishing or subscribing to object messages - logger_topic: str + logger_json_topic: str MQTT topic for publishing or subscribing to logger messages @@ -79,10 +79,10 @@ def __init__( """ # Parent class handles kwargs, including MQTT IP super().__init__(**kwargs) - self.config_topic = config_topic + self.config_json_topic = config_json_topic self.orientation_json_topic = orientation_json_topic self.object_json_topic = object_json_topic - self.logger_topic = logger_topic + self.logger_json_topic = logger_json_topic # Connect MQTT client logging.info("Connecting MQTT client") @@ -178,10 +178,10 @@ def make_handler() -> MessageHandler: """ handler = MessageHandler( mqtt_ip=os.getenv("MQTT_IP", ""), - config_topic=os.getenv("CONFIG_TOPIC", ""), + config_json_topic=os.getenv("CONFIG_JSON_TOPIC", ""), orientation_json_topic=os.getenv("ORIENTATION_JSON_TOPIC", ""), object_json_topic=os.getenv("OBJECT_JSON_TOPIC", ""), - logger_topic=os.getenv("LOGGER_TOPIC", ""), + logger_json_topic=os.getenv("LOGGER_JSON_TOPIC", ""), ) return handler @@ -219,7 +219,7 @@ def main() -> None: # Make the handler, and subscribe to the logger topic logging.info("Making the handler, and subscribing to topics") handler = make_handler() - handler.add_subscribe_topic(handler.logger_topic, handler._logger_callback) + handler.add_subscribe_topic(handler.logger_json_topic, handler._logger_callback) data = { "info": { "message": "Subscribed to the logger", @@ -238,7 +238,7 @@ def main() -> None: data_payload_type="Logger", data_payload=json.dumps(data), ) - handler.publish_to_topic(handler.logger_topic, logger_msg) + handler.publish_to_topic(handler.logger_json_topic, logger_msg) # Publish the configuration and orientation message, and the first # object message @@ -248,7 +248,7 @@ def main() -> None: index = 0 object_msg = make_object_msg(controller, track, index) logging.info(f"Publishing config msg: {config_msg}") - handler.publish_to_topic(handler.config_topic, config_msg) + handler.publish_to_topic(handler.config_json_topic, config_msg) time.sleep(LOOP_INTERVAL) logging.info(f"Publishing orientation msg: {orientation_msg}") diff --git a/axis-ptz-controller/test_modules.py b/axis-ptz-controller/test_modules.py index 0fb22f6..521e952 100644 --- a/axis-ptz-controller/test_modules.py +++ b/axis-ptz-controller/test_modules.py @@ -63,12 +63,12 @@ def controller() -> AxisPtzController: camera_user=os.getenv("CAMERA_USER", ""), camera_password=os.getenv("CAMERA_PASSWORD", ""), mqtt_ip=os.getenv("MQTT_IP", ""), - config_topic=os.getenv("CONFIG_TOPIC", ""), + config_json_topic=os.getenv("CONFIG_JSON_TOPIC", ""), orientation_json_topic=os.getenv("ORIENTATION_JSON_TOPIC", ""), object_json_topic=os.getenv("OBJECT_JSON_TOPIC", ""), image_bytestring_topic=os.getenv("IMAGE_BYTESTRING_TOPIC", ""), image_json_topic=os.getenv("IMAGE_JSON_TOPIC", ""), - logger_topic=os.getenv("LOGGER_TOPIC", ""), + logger_json_topic=os.getenv("LOGGER_JSON_TOPIC", ""), heartbeat_interval=HEARTBEAT_INTERVAL, loop_interval=LOOP_INTERVAL, capture_interval=CAPTURE_INTERVAL, From 20ae64bba413ff6dd6858101836f1383bca2db6f Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Tue, 8 Aug 2023 23:04:38 +0000 Subject: [PATCH 18/56] Update topics --- axis-ptz-controller.env | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/axis-ptz-controller.env b/axis-ptz-controller.env index c6977f9..c00bac8 100644 --- a/axis-ptz-controller.env +++ b/axis-ptz-controller.env @@ -1,19 +1,19 @@ -HOSTNAME=skyscan-011 MQTT_IP=mqtt +HOSTNAME=skyscan-011 CAMERA_IP=192.168.1.220 CAMERA_USER=root CAMERA_PASSWORD=iqtpass1 -CONFIG_JSON_TOPIC="skyscan/config/json" -ORIENTATION_JSON_TOPIC="skyscan/calibration/json" -OBJECT_JSON_TOPIC=/Multimodal/${HOSTNAME}/selection/edgetech-axis-ptz-controller/JSON -IMAGE_BYTESTRING_TOPIC="skyscan/encoded-image/json" -IMAGE_JSON_TOPIC="skyscan/image-metadata/json" -LOGGER_JSON_TOPIC="skyscan/logger/json" +CONFIG_JSON_TOPIC=/Multimodal/${HOSTNAME}/Config/edgetech-c2/JSON +ORIENTATION_JSON_TOPIC=/Multimodal/${HOSTNAME}/Orientation/edgetech-auto-orienter/JSON +OBJECT_JSON_TOPIC=/Multimodal/${HOSTNAME}/Object/edgetech-c2/JSON +IMAGE_BYTESTRING_TOPIC=/Multimodal/${HOSTNAME}/Base64Image/edgetech-axis-ptz-controller/ByteString +IMAGE_JSON_TOPIC=/Multimodal/${HOSTNAME}/ImageMetadata/edgetech-axis-ptz-controller/JSON +LOGGER_JSON_TOPIC=/Multimodal/${HOSTNAME}/Logger/edgetech-axis-ptz-controller/JSON HEARTBEAT_INTERVAL=10 TRIPOD_LATITUDE=38.89294 TRIPOD_LONGITUDE=-77.08529 TRIPOD_ALTITUDE=115.0 -UPDATE_INTERVAL=0.1 +LOOP_INTERVAL=0.1 CAPTURE_INTERVAL=2 CAPTURE_DIR=/data/tosort LEAD_TIME=0.5 @@ -25,11 +25,11 @@ TILT_RATE_MIN=1.8 TILT_RATE_MAX=150.0 FOCUS_SLOPE=0.0006 FOCUS_INTERCEPT=54 -JPEG_RESOLUTION="1920x1080" +JPEG_RESOLUTION=1920x1080 JPEG_COMPRESSION=5 USE_MQTT=True USE_CAMERA=False INCLUDE_AGE=True LOG_TO_MQTT=False CONTINUE_ON_EXCEPTION=False -LOG_LEVEL="DEBUG" +LOG_LEVEL=DEBUG From 48a81e3cc250a131decd83022254d7c43875d43e Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Thu, 10 Aug 2023 16:44:18 +0000 Subject: [PATCH 19/56] Add method to test camera connection --- axis-ptz-controller/camera_control.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/axis-ptz-controller/camera_control.py b/axis-ptz-controller/camera_control.py index c80c566..0793337 100644 --- a/axis-ptz-controller/camera_control.py +++ b/axis-ptz-controller/camera_control.py @@ -88,3 +88,17 @@ def set_focus(self, focus: Union[int, None] = None) -> str: """ return self._camera_command({"focus": focus}) + + def is_connected(self) -> bool: + """ + Operation to test camera connection. + + Returns: + Bool indicating connection, or not + + """ + resp = self._camera_command({"query": "position"}) + if resp.status_code == 200: + return True + else: + return False From 694241cdff9dab3d89d846067f2647d05197fa62 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Thu, 10 Aug 2023 16:44:38 +0000 Subject: [PATCH 20/56] Handle loss of camera connection gracefully --- axis-ptz-controller/axis_ptz_controller.py | 24 ++++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index c158546..c0d94a5 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -808,15 +808,21 @@ def _object_callback( logging.debug( f"Commanding pan and tilt rate indexes: {pan_rate_index}, {tilt_rate_index}" ) - self.camera_control.continuous_move( - pan_rate_index, - tilt_rate_index, - 0.0, - ) - if not self.do_capture: - logging.info(f"Starting image capture of object: {self.object_id}") - self.do_capture = True - self.capture_time = time() + if self.camera_control.is_connected(): + self.camera_control.continuous_move( + pan_rate_index, + tilt_rate_index, + 0.0, + ) + if not self.do_capture: + logging.info(f"Starting image capture of object: {self.object_id}") + self.do_capture = True + self.capture_time = time() + + else: + # Intialize the object id to point the camera at the + # object directly once the camera reconnects + self.object_id = "NA" # Log camera pointing using MQTT if self.log_to_mqtt: From b935a2c61ffa0f0ebe071aa4050d7485d0fd5eb1 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Sun, 27 Aug 2023 08:56:16 -0400 Subject: [PATCH 21/56] Make auto focus option --- axis-ptz-controller.env | 1 + axis-ptz-controller/axis_ptz_controller.py | 30 ++++++++++++++-------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/axis-ptz-controller.env b/axis-ptz-controller.env index 729fe3f..e394e26 100644 --- a/axis-ptz-controller.env +++ b/axis-ptz-controller.env @@ -27,6 +27,7 @@ JPEG_RESOLUTION=1920x1080 JPEG_COMPRESSION=5 USE_MQTT=True USE_CAMERA=False +AUTO_FOCUS=False INCLUDE_AGE=True LOG_TO_MQTT=False CONTINUE_ON_EXCEPTION=False diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index a7e7818..a556928 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -67,6 +67,7 @@ def __init__( jpeg_compression: int = 5, use_mqtt: bool = True, use_camera: bool = True, + auto_focus: bool = False, include_age: bool = True, log_to_mqtt: bool = False, continue_on_exception: bool = False, @@ -141,6 +142,8 @@ def __init__( Flag to use MQTT, or not use_camera: bool Flag to use camera configuration and control, or not + auto_focus: bool + Flag to auto focus, or not include_age: bool Flag to include object message age in lead time, or not log_to_mqtt: bool @@ -187,6 +190,7 @@ def __init__( self.jpeg_compression = jpeg_compression self.use_mqtt = use_mqtt self.use_camera = use_camera + self.auto_focus = auto_focus self.include_age = include_age self.log_to_mqtt = log_to_mqtt self.continue_on_exception = continue_on_exception @@ -452,6 +456,7 @@ def _config_callback( self.jpeg_compression = config.get("jpeg_compression", self.jpeg_compression) self.use_mqtt = config.get("use_mqtt", self.use_mqtt) self.use_camera = config.get("use_camera", self.use_camera) + self.auto_focus = config.get("auto_focus", self.auto_focus) self.include_age = config.get("include_age", self.include_age) self.log_to_mqtt = config.get("log_to_mqtt", self.log_to_mqtt) self.continue_on_exception = config.get( @@ -511,6 +516,7 @@ def _log_config(self: Any) -> None: "jpeg_compression": self.jpeg_compression, "use_mqtt": self.use_mqtt, "use_camera": self.use_camera, + "auto_focus": self.auto_focus, "include_age": self.include_age, "log_to_mqtt": self.log_to_mqtt, "continue_on_exception": self.continue_on_exception, @@ -768,17 +774,20 @@ def _object_callback( f"Camera pan and tilt rates: {self.rho_dot_c}, {self.tau_dot_c} [deg/s]" ) - # Compute and set focus, command camera pan and tilt rates, - # and begin capturing images, if needed + # Get, or compute and set focus, command camera pan and tilt + # rates, and begin capturing images, if needed if self.use_camera: - self.focus = int( - (self.focus_max - self.focus_min) - * (self.focus_slope * self.distance3d + self.focus_intercept) - / 100.0 - + self.focus_min - ) # [%] - logging.debug(f"Commanding focus: {self.focus}") - self.camera_control.set_focus(self.focus) + if self.auto_focus: + _rho_c, _tau_c, _zoom, self.focus = self.camera_control.get_ptz() + else: + self.focus = int( + (self.focus_max - self.focus_min) + * (self.focus_slope * self.distance3d + self.focus_intercept) + / 100.0 + + self.focus_min + ) # [%] + logging.debug(f"Commanding focus: {self.focus}") + self.camera_control.set_focus(self.focus) pan_rate_index = self._compute_pan_rate_index(self.rho_dot_c) tilt_rate_index = self._compute_tilt_rate_index(self.tau_dot_c) @@ -1157,6 +1166,7 @@ def make_controller() -> AxisPtzController: jpeg_compression=int(os.getenv("JPEG_COMPRESSION", 5)), use_mqtt=ast.literal_eval(os.getenv("USE_MQTT", "True")), use_camera=ast.literal_eval(os.getenv("USE_CAMERA", "True")), + auto_focus=ast.literal_eval(os.getenv("AUTO_FOCUS", "True")), include_age=ast.literal_eval(os.getenv("INCLUDE_AGE", "True")), log_to_mqtt=ast.literal_eval(os.getenv("LOG_TO_MQTT", "False")), continue_on_exception=ast.literal_eval( From f1c81d83ebfbb226f2f99d5e9b9f48bc1d259872 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Sun, 27 Aug 2023 09:20:53 -0400 Subject: [PATCH 22/56] Be defensive about setting focus --- axis-ptz-controller/axis_ptz_controller.py | 28 +++++++++++++++------- axis-ptz-controller/camera_control.py | 8 ++++--- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index a556928..001332b 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -354,10 +354,17 @@ def __init__( # Initialize camera pointing if self.use_camera: - logging.debug(f"Absolute move to pan: {self.rho_c}, and tilt: {self.tau_c}") - self.camera_control.absolute_move( - self.rho_c, self.tau_c, self.zoom, 50, self.focus + logging.info( + f"Absolute move to pan: {self.rho_c}, and tilt: {self.tau_c}, with zoom: {self.zoom}, and focus: {self.focus}" ) + if self.auto_focus: + self.camera_control.absolute_move( + self.rho_c, self.tau_c, self.zoom, 50 + ) + else: + self.camera_control.absolute_move( + self.rho_c, self.tau_c, self.zoom, 50, self.focus + ) # Log configuration parameters self._log_config() @@ -711,9 +718,14 @@ def _object_callback( logging.info( f"Absolute move to pan: {self.rho_o}, and tilt: {self.tau_o}, with zoom: {self.zoom}, and focus: {self.focus}" ) - self.camera_control.absolute_move( - self.rho_o, self.tau_o, self.zoom, 50, self.focus - ) + if self.auto_focus: + self.camera_control.absolute_move( + self.rho_o, self.tau_o, self.zoom, 50 + ) + else: + self.camera_control.absolute_move( + self.rho_o, self.tau_o, self.zoom, 50, self.focus + ) duration = max( math.fabs(self.rho_c - self.rho_o) / (self.pan_rate_max / 2), math.fabs(self.tau_c - self.tau_o) / (self.tilt_rate_max / 2), @@ -777,9 +789,7 @@ def _object_callback( # Get, or compute and set focus, command camera pan and tilt # rates, and begin capturing images, if needed if self.use_camera: - if self.auto_focus: - _rho_c, _tau_c, _zoom, self.focus = self.camera_control.get_ptz() - else: + if not self.auto_focus: self.focus = int( (self.focus_max - self.focus_min) * (self.focus_slope * self.distance3d + self.focus_intercept) diff --git a/axis-ptz-controller/camera_control.py b/axis-ptz-controller/camera_control.py index 0793337..e481717 100644 --- a/axis-ptz-controller/camera_control.py +++ b/axis-ptz-controller/camera_control.py @@ -55,9 +55,11 @@ def absolute_move( Returns the response from the device to the command sent. """ - return self._camera_command( - {"pan": pan, "tilt": tilt, "zoom": zoom, "speed": speed, "focus": focus} - ) + command = {"pan": pan, "tilt": tilt, "zoom": zoom, "speed": speed} + # Defensively only set focus if needed + if focus is not None: + command["focus"] = focus + return self._camera_command(command) def get_ptz(self) -> Tuple[float, float, float, float]: """ From a008232870342a40e6361166e856ca98e4bb9704 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Sun, 27 Aug 2023 14:09:46 +0000 Subject: [PATCH 23/56] Improve logging of focus setting, or not --- axis-ptz-controller/axis_ptz_controller.py | 18 ++++++++++++------ axis-ptz-controller/camera_control.py | 1 + 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 001332b..15c5acd 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -354,14 +354,17 @@ def __init__( # Initialize camera pointing if self.use_camera: - logging.info( - f"Absolute move to pan: {self.rho_c}, and tilt: {self.tau_c}, with zoom: {self.zoom}, and focus: {self.focus}" - ) if self.auto_focus: + logging.info( + f"Absolute move to pan: {self.rho_c}, and tilt: {self.tau_c}, with zoom: {self.zoom}" + ) self.camera_control.absolute_move( self.rho_c, self.tau_c, self.zoom, 50 ) else: + logging.info( + f"Absolute move to pan: {self.rho_c}, and tilt: {self.tau_c}, with zoom: {self.zoom}, and focus: {self.focus}" + ) self.camera_control.absolute_move( self.rho_c, self.tau_c, self.zoom, 50, self.focus ) @@ -715,14 +718,17 @@ def _object_callback( # Point the camera at any new object directly if self.object_id != object_id: self.object_id = object_id - logging.info( - f"Absolute move to pan: {self.rho_o}, and tilt: {self.tau_o}, with zoom: {self.zoom}, and focus: {self.focus}" - ) if self.auto_focus: + logging.info( + f"Absolute move to pan: {self.rho_o}, and tilt: {self.tau_o}, with zoom: {self.zoom}" + ) self.camera_control.absolute_move( self.rho_o, self.tau_o, self.zoom, 50 ) else: + logging.info( + f"Absolute move to pan: {self.rho_o}, and tilt: {self.tau_o}, with zoom: {self.zoom}, and focus: {self.focus}" + ) self.camera_control.absolute_move( self.rho_o, self.tau_o, self.zoom, 50, self.focus ) diff --git a/axis-ptz-controller/camera_control.py b/axis-ptz-controller/camera_control.py index e481717..7511881 100644 --- a/axis-ptz-controller/camera_control.py +++ b/axis-ptz-controller/camera_control.py @@ -59,6 +59,7 @@ def absolute_move( # Defensively only set focus if needed if focus is not None: command["focus"] = focus + logging.info(f"command: {command}") return self._camera_command(command) def get_ptz(self) -> Tuple[float, float, float, float]: From 644a6c611dde26f1decc4814450c34b7fe7afa32 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Sun, 27 Aug 2023 11:00:28 -0400 Subject: [PATCH 24/56] Blacken buffer --- axis-ptz-controller/axis_ptz_controller.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 15c5acd..5fd0bd4 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -358,9 +358,7 @@ def __init__( logging.info( f"Absolute move to pan: {self.rho_c}, and tilt: {self.tau_c}, with zoom: {self.zoom}" ) - self.camera_control.absolute_move( - self.rho_c, self.tau_c, self.zoom, 50 - ) + self.camera_control.absolute_move(self.rho_c, self.tau_c, self.zoom, 50) else: logging.info( f"Absolute move to pan: {self.rho_c}, and tilt: {self.tau_c}, with zoom: {self.zoom}, and focus: {self.focus}" From 8a8a8fe718fecd8ec9b036ea284ef5531a4ddd37 Mon Sep 17 00:00:00 2001 From: spearw Date: Thu, 31 Aug 2023 13:48:00 -0400 Subject: [PATCH 25/56] Add test-harness --- tests/docker-compose.yaml | 36 + tests/test-data/test-input.json | 2531 +++++++++++++++++++++++++++++++ tests/test-harness.env | 7 + tests/test.sh | 15 + tests/test_integration.py | 70 + 5 files changed, 2659 insertions(+) create mode 100644 tests/docker-compose.yaml create mode 100644 tests/test-data/test-input.json create mode 100644 tests/test-harness.env create mode 100644 tests/test.sh create mode 100644 tests/test_integration.py diff --git a/tests/docker-compose.yaml b/tests/docker-compose.yaml new file mode 100644 index 0000000..a6e4342 --- /dev/null +++ b/tests/docker-compose.yaml @@ -0,0 +1,36 @@ +# Prior to building, copy the following resources from +# IQTLabs/edgetech-core to the build directory: +# - core/base_mqtt_pub_sub.py +# - mqtt/* +# Clone iqt/labs/edgetech-test-harness and build test-harness image + +version: "3" +services: + test-harness: + image: iqtlabs/edgetech-test-harness:latest + volumes: + - ${PWD}/test-data:/test-data + restart: on-failure + depends_on: + - mqtt + - axis-ptz-controller + env_file: + - test-harness.env + mqtt: + image: iqtlabs/edgetech-mqtt:latest + ports: + - "1883:1883" + - "9001:9001" + restart: unless-stopped + axis-ptz-controller: + image: iqtlabs/edgetech-axis-ptz-controller:latest + build: + context: ../axis-ptz-controller + dockerfile: ./Dockerfile + restart: unless-stopped + depends_on: + - mqtt + env_file: + - axis-ptz-controller.env + volumes: + - ./test-data/tosort:/data/tosort \ No newline at end of file diff --git a/tests/test-data/test-input.json b/tests/test-data/test-input.json new file mode 100644 index 0000000..5fa89af --- /dev/null +++ b/tests/test-data/test-input.json @@ -0,0 +1,2531 @@ +[ +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439244, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520715, \"altitude\": 1181.1000000000001, \"ts_altitude\": 1670520714.7, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 39.055984, \"longitude\": -77.357423, \"timestamp\": 1670520714.7, \"vertical_velocity\": -0.32512, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439245, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520716, \"altitude\": 1181.1000000000001, \"ts_altitude\": 1670520715.7, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 39.055984, \"longitude\": -77.357423, \"timestamp\": 1670520715.7, \"vertical_velocity\": -0.32512, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439246, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520717, \"altitude\": 1181.1000000000001, \"ts_altitude\": 1670520716.9, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 39.04946, \"longitude\": -77.357422, \"timestamp\": 1670520716.9, \"vertical_velocity\": -0.32512, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439247, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520718, \"altitude\": 1173.48, \"ts_altitude\": 1670520718.0, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 39.048203, \"longitude\": -77.357423, \"timestamp\": 1670520718.0, \"vertical_velocity\": -0.32512, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439248, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520719, \"altitude\": 1173.48, \"ts_altitude\": 1670520718.9, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 39.046993, \"longitude\": -77.357361, \"timestamp\": 1670520718.9, \"vertical_velocity\": 0.0, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439251, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520722, \"altitude\": 1173.48, \"ts_altitude\": 1670520721.9, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 39.043408, \"longitude\": -77.357361, \"timestamp\": 1670520721.9, \"vertical_velocity\": 0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439252, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520723, \"altitude\": 1173.48, \"ts_altitude\": 1670520722.7, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 39.042058, \"longitude\": -77.357361, \"timestamp\": 1670520722.7, \"vertical_velocity\": -0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439254, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520724, \"altitude\": 1173.48, \"ts_altitude\": 1670520723.9, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 39.042058, \"longitude\": -77.357361, \"timestamp\": 1670520723.9, \"vertical_velocity\": -0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439255, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520725, \"altitude\": 1173.48, \"ts_altitude\": 1670520724.9, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 39.039322, \"longitude\": -77.357363, \"timestamp\": 1670520724.9, \"vertical_velocity\": -0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439255, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520726, \"altitude\": 1173.48, \"ts_altitude\": 1670520725.8, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 39.038055, \"longitude\": -77.357361, \"timestamp\": 1670520725.8, \"vertical_velocity\": -0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439259, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520729, \"altitude\": 1173.48, \"ts_altitude\": 1670520728.8, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 39.034286, \"longitude\": -77.357363, \"timestamp\": 1670520728.8, \"vertical_velocity\": -0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439260, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520731, \"altitude\": 1173.48, \"ts_altitude\": 1670520730.6, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 39.034286, \"longitude\": -77.357363, \"timestamp\": 1670520730.6, \"vertical_velocity\": -0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439262, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520732, \"altitude\": 1173.48, \"ts_altitude\": 1670520731.9, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 39.034286, \"longitude\": -77.357363, \"timestamp\": 1670520731.9, \"vertical_velocity\": -0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439262, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520733, \"altitude\": 1173.48, \"ts_altitude\": 1670520732.3, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 39.029675, \"longitude\": -77.357361, \"timestamp\": 1670520732.3, \"vertical_velocity\": -0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439264, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520734, \"altitude\": 1173.48, \"ts_altitude\": 1670520733.9, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 39.029675, \"longitude\": -77.357361, \"timestamp\": 1670520733.9, \"vertical_velocity\": -0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439265, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520735, \"altitude\": 1173.48, \"ts_altitude\": 1670520734.9, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 39.027208, \"longitude\": -77.357361, \"timestamp\": 1670520734.9, \"vertical_velocity\": -0.32512, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439266, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520736, \"altitude\": 1173.48, \"ts_altitude\": 1670520735.9, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 39.025955, \"longitude\": -77.357363, \"timestamp\": 1670520735.9, \"vertical_velocity\": 0.0, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439267, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520737, \"altitude\": 1173.48, \"ts_altitude\": 1670520736.7, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 39.024741, \"longitude\": -77.357361, \"timestamp\": 1670520736.7, \"vertical_velocity\": 0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439268, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520738, \"altitude\": 1173.48, \"ts_altitude\": 1670520737.7, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 39.023483, \"longitude\": -77.357363, \"timestamp\": 1670520737.7, \"vertical_velocity\": 0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439269, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520739, \"altitude\": 1173.48, \"ts_altitude\": 1670520738.8, \"horizontal_velocity\": 138.38565837333334, \"object_id\": \"a1e946\", \"latitude\": 39.023483, \"longitude\": -77.357363, \"timestamp\": 1670520738.8, \"vertical_velocity\": 0.97536, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439270, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520740, \"altitude\": 1173.48, \"ts_altitude\": 1670520739.8, \"horizontal_velocity\": 138.38565837333334, \"object_id\": \"a1e946\", \"latitude\": 39.021011, \"longitude\": -77.357304, \"timestamp\": 1670520739.8, \"vertical_velocity\": 0.65024, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439271, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520741, \"altitude\": 1173.48, \"ts_altitude\": 1670520740.8, \"horizontal_velocity\": 138.38565837333334, \"object_id\": \"a1e946\", \"latitude\": 39.021011, \"longitude\": -77.357304, \"timestamp\": 1670520740.8, \"vertical_velocity\": 0.65024, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439274, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520744, \"altitude\": 1173.48, \"ts_altitude\": 1670520743.7, \"horizontal_velocity\": 138.38565837333334, \"object_id\": \"a1e946\", \"latitude\": 39.016268, \"longitude\": -77.3573, \"timestamp\": 1670520743.7, \"vertical_velocity\": 0.65024, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439275, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520745, \"altitude\": 1173.48, \"ts_altitude\": 1670520744.8, \"horizontal_velocity\": 138.38565837333334, \"object_id\": \"a1e946\", \"latitude\": 39.016268, \"longitude\": -77.3573, \"timestamp\": 1670520744.8, \"vertical_velocity\": 0.65024, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439277, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520747, \"altitude\": 1173.48, \"ts_altitude\": 1670520746.9, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 39.012497, \"longitude\": -77.357304, \"timestamp\": 1670520746.9, \"vertical_velocity\": -0.65024, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439279, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520748, \"altitude\": 1181.1000000000001, \"ts_altitude\": 1670520748.0, \"horizontal_velocity\": 137.35676872000002, \"object_id\": \"a1e946\", \"latitude\": 39.011032, \"longitude\": -77.357304, \"timestamp\": 1670520748.0, \"vertical_velocity\": -2.60096, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439280, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520749, \"altitude\": 1165.8600000000001, \"ts_altitude\": 1670520749.0, \"horizontal_velocity\": 137.35676872000002, \"object_id\": \"a1e946\", \"latitude\": 39.009933, \"longitude\": -77.357304, \"timestamp\": 1670520749.0, \"vertical_velocity\": -3.2512000000000003, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439281, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520750, \"altitude\": 1173.48, \"ts_altitude\": 1670520749.9, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 39.00856, \"longitude\": -77.357304, \"timestamp\": 1670520749.9, \"vertical_velocity\": -4.55168, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439281, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520751, \"altitude\": 1165.8600000000001, \"ts_altitude\": 1670520750.1, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 39.00856, \"longitude\": -77.357304, \"timestamp\": 1670520750.1, \"vertical_velocity\": -6.1772800000000005, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439282, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520752, \"altitude\": 1165.8600000000001, \"ts_altitude\": 1670520751.6, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 39.00856, \"longitude\": -77.357304, \"timestamp\": 1670520751.6, \"vertical_velocity\": -6.1772800000000005, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439284, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520753, \"altitude\": 1143.0, \"ts_altitude\": 1670520752.8, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 39.00491, \"longitude\": -77.357239, \"timestamp\": 1670520752.8, \"vertical_velocity\": -8.128, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439285, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520754, \"altitude\": 1135.38, \"ts_altitude\": 1670520753.9, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 39.004351, \"longitude\": -77.357239, \"timestamp\": 1670520753.9, \"vertical_velocity\": -8.128, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439286, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520755, \"altitude\": 1127.76, \"ts_altitude\": 1670520754.9, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 39.002609, \"longitude\": -77.357244, \"timestamp\": 1670520754.9, \"vertical_velocity\": -8.128, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439286, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520756, \"altitude\": 1127.76, \"ts_altitude\": 1670520755.6, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 39.001236, \"longitude\": -77.357244, \"timestamp\": 1670520755.6, \"vertical_velocity\": -8.128, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439288, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520757, \"altitude\": 1104.9, \"ts_altitude\": 1670520757.0, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.999417, \"longitude\": -77.357239, \"timestamp\": 1670520757.0, \"vertical_velocity\": -8.45312, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439289, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520758, \"altitude\": 1097.28, \"ts_altitude\": 1670520758.0, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.99826, \"longitude\": -77.357244, \"timestamp\": 1670520758.0, \"vertical_velocity\": -8.77824, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439290, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520759, \"altitude\": 1089.66, \"ts_altitude\": 1670520758.8, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.996996, \"longitude\": -77.357239, \"timestamp\": 1670520758.8, \"vertical_velocity\": -9.10336, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439291, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520760, \"altitude\": 1089.66, \"ts_altitude\": 1670520759.8, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.996292, \"longitude\": -77.357244, \"timestamp\": 1670520759.8, \"vertical_velocity\": -9.10336, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439291, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520761, \"altitude\": 1089.66, \"ts_altitude\": 1670520760.1, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.996292, \"longitude\": -77.357244, \"timestamp\": 1670520760.1, \"vertical_velocity\": -9.10336, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439293, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520762, \"altitude\": 1082.04, \"ts_altitude\": 1670520762.0, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.996292, \"longitude\": -77.357244, \"timestamp\": 1670520762.0, \"vertical_velocity\": -9.7536, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439294, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520763, \"altitude\": 1051.56, \"ts_altitude\": 1670520762.9, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.992154, \"longitude\": -77.357239, \"timestamp\": 1670520762.9, \"vertical_velocity\": -10.07872, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439295, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520764, \"altitude\": 1036.3200000000002, \"ts_altitude\": 1670520763.8, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.990936, \"longitude\": -77.357184, \"timestamp\": 1670520763.8, \"vertical_velocity\": -10.40384, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439296, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520765, \"altitude\": 1028.7, \"ts_altitude\": 1670520764.9, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.989687, \"longitude\": -77.357178, \"timestamp\": 1670520764.9, \"vertical_velocity\": -10.72896, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439297, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520766, \"altitude\": 1013.46, \"ts_altitude\": 1670520765.9, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.988464, \"longitude\": -77.357184, \"timestamp\": 1670520765.9, \"vertical_velocity\": -10.72896, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439298, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520767, \"altitude\": 1013.46, \"ts_altitude\": 1670520766.9, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.987732, \"longitude\": -77.357178, \"timestamp\": 1670520766.9, \"vertical_velocity\": -10.40384, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439299, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520768, \"altitude\": 1013.46, \"ts_altitude\": 1670520767.9, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.987732, \"longitude\": -77.357178, \"timestamp\": 1670520767.9, \"vertical_velocity\": -10.40384, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439300, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520769, \"altitude\": 1005.84, \"ts_altitude\": 1670520768.9, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.985992, \"longitude\": -77.357184, \"timestamp\": 1670520768.9, \"vertical_velocity\": -10.07872, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439301, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520770, \"altitude\": 990.6, \"ts_altitude\": 1670520770.0, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.984241, \"longitude\": -77.357178, \"timestamp\": 1670520770.0, \"vertical_velocity\": -9.7536, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439302, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520771, \"altitude\": 975.36, \"ts_altitude\": 1670520770.8, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.983017, \"longitude\": -77.357184, \"timestamp\": 1670520770.8, \"vertical_velocity\": -9.10336, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439304, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520772, \"altitude\": 967.74, \"ts_altitude\": 1670520772.0, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.98182, \"longitude\": -77.357178, \"timestamp\": 1670520772.0, \"vertical_velocity\": -8.77824, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439304, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520773, \"altitude\": 960.12, \"ts_altitude\": 1670520772.7, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.980749, \"longitude\": -77.357178, \"timestamp\": 1670520772.7, \"vertical_velocity\": -8.45312, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439306, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520774, \"altitude\": 952.5, \"ts_altitude\": 1670520773.9, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.979353, \"longitude\": -77.357178, \"timestamp\": 1670520773.9, \"vertical_velocity\": -8.128, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439307, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520775, \"altitude\": 944.88, \"ts_altitude\": 1670520774.9, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.978119, \"longitude\": -77.357125, \"timestamp\": 1670520774.9, \"vertical_velocity\": -8.128, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439307, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520776, \"altitude\": 937.26, \"ts_altitude\": 1670520775.7, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.976839, \"longitude\": -77.357117, \"timestamp\": 1670520775.7, \"vertical_velocity\": -8.45312, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439309, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520777, \"altitude\": 929.6400000000001, \"ts_altitude\": 1670520777.0, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.975555, \"longitude\": -77.357125, \"timestamp\": 1670520777.0, \"vertical_velocity\": -7.80288, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439310, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520778, \"altitude\": 922.0200000000001, \"ts_altitude\": 1670520778.0, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.974418, \"longitude\": -77.357117, \"timestamp\": 1670520778.0, \"vertical_velocity\": -7.477760000000001, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439311, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520779, \"altitude\": 914.4000000000001, \"ts_altitude\": 1670520778.9, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.972928, \"longitude\": -77.357117, \"timestamp\": 1670520778.9, \"vertical_velocity\": -6.827520000000001, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439312, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520780, \"altitude\": 906.7800000000001, \"ts_altitude\": 1670520779.9, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.972214, \"longitude\": -77.357125, \"timestamp\": 1670520779.9, \"vertical_velocity\": -6.1772800000000005, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439313, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520781, \"altitude\": 899.1600000000001, \"ts_altitude\": 1670520780.9, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.970703, \"longitude\": -77.357125, \"timestamp\": 1670520780.9, \"vertical_velocity\": -5.52704, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439314, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520782, \"altitude\": 891.5400000000001, \"ts_altitude\": 1670520781.9, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.96939, \"longitude\": -77.357117, \"timestamp\": 1670520781.9, \"vertical_velocity\": -4.8768, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439315, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520783, \"altitude\": 883.9200000000001, \"ts_altitude\": 1670520782.8, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.967682, \"longitude\": -77.357125, \"timestamp\": 1670520782.8, \"vertical_velocity\": -3.90144, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439316, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520784, \"altitude\": 883.9200000000001, \"ts_altitude\": 1670520783.9, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.966309, \"longitude\": -77.357125, \"timestamp\": 1670520783.9, \"vertical_velocity\": -3.2512000000000003, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439317, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520785, \"altitude\": 883.9200000000001, \"ts_altitude\": 1670520784.9, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.965061, \"longitude\": -77.357117, \"timestamp\": 1670520784.9, \"vertical_velocity\": -2.60096, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439318, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520786, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520785.9, \"horizontal_velocity\": 135.81343424, \"object_id\": \"a1e946\", \"latitude\": 38.963745, \"longitude\": -77.357125, \"timestamp\": 1670520785.9, \"vertical_velocity\": -1.95072, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439319, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520787, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520786.9, \"horizontal_velocity\": 135.81343424, \"object_id\": \"a1e946\", \"latitude\": 38.962547, \"longitude\": -77.357117, \"timestamp\": 1670520786.9, \"vertical_velocity\": -1.6256000000000002, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439320, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520788, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520788.0, \"horizontal_velocity\": 135.81343424, \"object_id\": \"a1e946\", \"latitude\": 38.961411, \"longitude\": -77.357125, \"timestamp\": 1670520788.0, \"vertical_velocity\": -0.97536, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439321, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520789, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520788.9, \"horizontal_velocity\": 135.81343424, \"object_id\": \"a1e946\", \"latitude\": 38.960173, \"longitude\": -77.357117, \"timestamp\": 1670520788.9, \"vertical_velocity\": -0.65024, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439322, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520790, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520789.9, \"horizontal_velocity\": 135.29898941333335, \"object_id\": \"a1e946\", \"latitude\": 38.958984, \"longitude\": -77.357125, \"timestamp\": 1670520789.9, \"vertical_velocity\": -0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439323, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520791, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520791.0, \"horizontal_velocity\": 135.29898941333335, \"object_id\": \"a1e946\", \"latitude\": 38.957752, \"longitude\": -77.357117, \"timestamp\": 1670520791.0, \"vertical_velocity\": -0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439324, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520792, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520791.9, \"horizontal_velocity\": 135.29898941333335, \"object_id\": \"a1e946\", \"latitude\": 38.956512, \"longitude\": -77.357125, \"timestamp\": 1670520791.9, \"vertical_velocity\": -0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439325, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520793, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520793.0, \"horizontal_velocity\": 135.29898941333335, \"object_id\": \"a1e946\", \"latitude\": 38.955192, \"longitude\": -77.357117, \"timestamp\": 1670520793.0, \"vertical_velocity\": -0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439326, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520794, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520793.9, \"horizontal_velocity\": 135.29898941333335, \"object_id\": \"a1e946\", \"latitude\": 38.953795, \"longitude\": -77.357117, \"timestamp\": 1670520793.9, \"vertical_velocity\": -0.32512, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439327, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520795, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520794.9, \"horizontal_velocity\": 135.29898941333335, \"object_id\": \"a1e946\", \"latitude\": 38.953795, \"longitude\": -77.357117, \"timestamp\": 1670520794.9, \"vertical_velocity\": -0.32512, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439328, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520796, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520795.8, \"horizontal_velocity\": 135.29898941333335, \"object_id\": \"a1e946\", \"latitude\": 38.952576, \"longitude\": -77.357065, \"timestamp\": 1670520795.8, \"vertical_velocity\": -0.65024, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439329, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520797, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520796.8, \"horizontal_velocity\": 135.29898941333335, \"object_id\": \"a1e946\", \"latitude\": 38.951375, \"longitude\": -77.357056, \"timestamp\": 1670520796.8, \"vertical_velocity\": -0.97536, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439331, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520798, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520797.9, \"horizontal_velocity\": 135.29898941333335, \"object_id\": \"a1e946\", \"latitude\": 38.95015, \"longitude\": -77.357065, \"timestamp\": 1670520797.9, \"vertical_velocity\": -0.97536, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439331, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520799, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520798.8, \"horizontal_velocity\": 135.29898941333335, \"object_id\": \"a1e946\", \"latitude\": 38.948907, \"longitude\": -77.357056, \"timestamp\": 1670520798.8, \"vertical_velocity\": -0.97536, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439333, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520800, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520799.9, \"horizontal_velocity\": 135.29898941333335, \"object_id\": \"a1e946\", \"latitude\": 38.947678, \"longitude\": -77.357065, \"timestamp\": 1670520799.9, \"vertical_velocity\": -0.65024, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439334, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520801, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520800.9, \"horizontal_velocity\": 135.29898941333335, \"object_id\": \"a1e946\", \"latitude\": 38.94644, \"longitude\": -77.357056, \"timestamp\": 1670520800.9, \"vertical_velocity\": -0.65024, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439335, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520802, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520802.0, \"horizontal_velocity\": 135.29898941333335, \"object_id\": \"a1e946\", \"latitude\": 38.945251, \"longitude\": -77.357065, \"timestamp\": 1670520802.0, \"vertical_velocity\": -0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439336, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520803, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520803.0, \"horizontal_velocity\": 135.29898941333335, \"object_id\": \"a1e946\", \"latitude\": 38.943973, \"longitude\": -77.357056, \"timestamp\": 1670520803.0, \"vertical_velocity\": 0.0, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439337, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520804, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520803.9, \"horizontal_velocity\": 135.29898941333335, \"object_id\": \"a1e946\", \"latitude\": 38.942276, \"longitude\": -77.357065, \"timestamp\": 1670520803.9, \"vertical_velocity\": 0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439338, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520805, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520804.9, \"horizontal_velocity\": 135.29898941333335, \"object_id\": \"a1e946\", \"latitude\": 38.94104, \"longitude\": -77.357056, \"timestamp\": 1670520804.9, \"vertical_velocity\": 0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439339, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520806, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520805.8, \"horizontal_velocity\": 135.81343424, \"object_id\": \"a1e946\", \"latitude\": 38.93985, \"longitude\": -77.357065, \"timestamp\": 1670520805.8, \"vertical_velocity\": 0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439340, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520807, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520806.9, \"horizontal_velocity\": 135.81343424, \"object_id\": \"a1e946\", \"latitude\": 38.938619, \"longitude\": -77.357056, \"timestamp\": 1670520806.9, \"vertical_velocity\": 0.65024, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439341, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520808, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520807.9, \"horizontal_velocity\": 135.81343424, \"object_id\": \"a1e946\", \"latitude\": 38.937378, \"longitude\": -77.357065, \"timestamp\": 1670520807.9, \"vertical_velocity\": 0.65024, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439342, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520809, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520809.0, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.936012, \"longitude\": -77.357056, \"timestamp\": 1670520809.0, \"vertical_velocity\": 0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439343, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520810, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520809.9, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.934662, \"longitude\": -77.357056, \"timestamp\": 1670520809.9, \"vertical_velocity\": 0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439344, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520811, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520811.0, \"horizontal_velocity\": 136.32787906666667, \"object_id\": \"a1e946\", \"latitude\": 38.933441, \"longitude\": -77.357065, \"timestamp\": 1670520811.0, \"vertical_velocity\": -0.65024, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439345, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520812, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520811.8, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.932195, \"longitude\": -77.357056, \"timestamp\": 1670520811.8, \"vertical_velocity\": -0.97536, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439346, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520813, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520812.8, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.930969, \"longitude\": -77.357065, \"timestamp\": 1670520812.8, \"vertical_velocity\": -0.97536, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439347, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520814, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520814.0, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.929728, \"longitude\": -77.357056, \"timestamp\": 1670520814.0, \"vertical_velocity\": -0.65024, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439348, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520815, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520814.9, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.92875, \"longitude\": -77.357056, \"timestamp\": 1670520814.9, \"vertical_velocity\": 0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439349, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520816, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520815.6, \"horizontal_velocity\": 137.35676872000002, \"object_id\": \"a1e946\", \"latitude\": 38.92749, \"longitude\": -77.357065, \"timestamp\": 1670520815.6, \"vertical_velocity\": 1.6256000000000002, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439350, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520817, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520817.0, \"horizontal_velocity\": 137.35676872000002, \"object_id\": \"a1e946\", \"latitude\": 38.925631, \"longitude\": -77.357056, \"timestamp\": 1670520817.0, \"vertical_velocity\": 1.6256000000000002, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439351, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520818, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520818.0, \"horizontal_velocity\": 137.35676872000002, \"object_id\": \"a1e946\", \"latitude\": 38.924515, \"longitude\": -77.357065, \"timestamp\": 1670520818.0, \"vertical_velocity\": 0.65024, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439352, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520819, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520818.9, \"horizontal_velocity\": 137.35676872000002, \"object_id\": \"a1e946\", \"latitude\": 38.923816, \"longitude\": -77.357056, \"timestamp\": 1670520818.9, \"vertical_velocity\": -1.30048, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439353, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520820, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520819.8, \"horizontal_velocity\": 137.35676872000002, \"object_id\": \"a1e946\", \"latitude\": 38.921997, \"longitude\": -77.357065, \"timestamp\": 1670520819.8, \"vertical_velocity\": -1.6256000000000002, \"track\": 180.2, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439354, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520821, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520820.9, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.920836, \"longitude\": -77.357056, \"timestamp\": 1670520820.9, \"vertical_velocity\": -1.6256000000000002, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439355, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520822, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520821.8, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.919479, \"longitude\": -77.357065, \"timestamp\": 1670520821.8, \"vertical_velocity\": -1.6256000000000002, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439356, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520823, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520822.8, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.919479, \"longitude\": -77.357065, \"timestamp\": 1670520822.8, \"vertical_velocity\": -1.6256000000000002, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439358, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520824, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520823.9, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.918229, \"longitude\": -77.357056, \"timestamp\": 1670520823.9, \"vertical_velocity\": -0.65024, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439359, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520825, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520825.0, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.917099, \"longitude\": -77.357065, \"timestamp\": 1670520825.0, \"vertical_velocity\": -0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439360, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520826, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520826.0, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.915762, \"longitude\": -77.357056, \"timestamp\": 1670520826.0, \"vertical_velocity\": 0.0, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439361, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520827, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520827.0, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.914124, \"longitude\": -77.357065, \"timestamp\": 1670520827.0, \"vertical_velocity\": 0.0, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439362, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520828, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520827.9, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.912876, \"longitude\": -77.357056, \"timestamp\": 1670520827.9, \"vertical_velocity\": 0.0, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439363, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520829, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520828.8, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.911652, \"longitude\": -77.357065, \"timestamp\": 1670520828.8, \"vertical_velocity\": -0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439364, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520830, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520829.9, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.910316, \"longitude\": -77.357056, \"timestamp\": 1670520829.9, \"vertical_velocity\": -0.65024, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439365, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520831, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520830.9, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.909042, \"longitude\": -77.357065, \"timestamp\": 1670520830.9, \"vertical_velocity\": -0.65024, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439366, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520832, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520831.9, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.907669, \"longitude\": -77.357065, \"timestamp\": 1670520831.9, \"vertical_velocity\": -0.65024, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439367, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520833, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520833.0, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.906452, \"longitude\": -77.357056, \"timestamp\": 1670520833.0, \"vertical_velocity\": 0.0, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439368, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520834, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520833.8, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.905334, \"longitude\": -77.357056, \"timestamp\": 1670520833.8, \"vertical_velocity\": 0.0, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439369, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520835, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520835.0, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.903938, \"longitude\": -77.357056, \"timestamp\": 1670520835.0, \"vertical_velocity\": 0.32512, \"track\": 179.8, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439370, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520836, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520835.9, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.902588, \"longitude\": -77.357065, \"timestamp\": 1670520835.9, \"vertical_velocity\": 0.32512, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439371, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520837, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520836.7, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.901471, \"longitude\": -77.357056, \"timestamp\": 1670520836.7, \"vertical_velocity\": 0.65024, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439372, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520838, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520837.8, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.900208, \"longitude\": -77.357065, \"timestamp\": 1670520837.8, \"vertical_velocity\": 0.65024, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439373, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520839, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520838.9, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.899003, \"longitude\": -77.357056, \"timestamp\": 1670520838.9, \"vertical_velocity\": 0.65024, \"track\": 180.0, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439374, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520840, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520839.8, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.89784, \"longitude\": -77.357056, \"timestamp\": 1670520839.8, \"vertical_velocity\": 0.97536, \"track\": 180.2, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439375, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520841, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520840.9, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.895977, \"longitude\": -77.357056, \"timestamp\": 1670520840.9, \"vertical_velocity\": 0.65024, \"track\": 180.4, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439376, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520842, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520841.9, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.895, \"longitude\": -77.357056, \"timestamp\": 1670520841.9, \"vertical_velocity\": 0.32512, \"track\": 180.4, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439377, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520843, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520842.9, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.893753, \"longitude\": -77.357065, \"timestamp\": 1670520842.9, \"vertical_velocity\": -0.32512, \"track\": 180.6, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439379, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520844, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520844.0, \"horizontal_velocity\": 138.38565837333334, \"object_id\": \"a1e946\", \"latitude\": 38.892486, \"longitude\": -77.357117, \"timestamp\": 1670520844.0, \"vertical_velocity\": -0.32512, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439379, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520845, \"altitude\": 876.3000000000001, \"ts_altitude\": 1670520844.9, \"horizontal_velocity\": 138.38565837333334, \"object_id\": \"a1e946\", \"latitude\": 38.891235, \"longitude\": -77.357125, \"timestamp\": 1670520844.9, \"vertical_velocity\": -0.65024, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439380, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520846, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520845.7, \"horizontal_velocity\": 138.38565837333334, \"object_id\": \"a1e946\", \"latitude\": 38.890019, \"longitude\": -77.357178, \"timestamp\": 1670520845.7, \"vertical_velocity\": -0.65024, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439382, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520847, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520847.0, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.88826, \"longitude\": -77.357184, \"timestamp\": 1670520847.0, \"vertical_velocity\": -0.65024, \"track\": 181.1, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439383, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520848, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520847.9, \"horizontal_velocity\": 137.87121354666667, \"object_id\": \"a1e946\", \"latitude\": 38.886993, \"longitude\": -77.357239, \"timestamp\": 1670520847.9, \"vertical_velocity\": -0.65024, \"track\": 181.1, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439384, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520849, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520848.9, \"horizontal_velocity\": 137.92265802933335, \"object_id\": \"a1e946\", \"latitude\": 38.886434, \"longitude\": -77.357239, \"timestamp\": 1670520848.9, \"vertical_velocity\": -0.32512, \"track\": 181.3, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439385, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520850, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520849.9, \"horizontal_velocity\": 137.92265802933335, \"object_id\": \"a1e946\", \"latitude\": 38.884479, \"longitude\": -77.3573, \"timestamp\": 1670520849.9, \"vertical_velocity\": -0.32512, \"track\": 181.3, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439386, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520851, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520850.9, \"horizontal_velocity\": 137.92265802933335, \"object_id\": \"a1e946\", \"latitude\": 38.884479, \"longitude\": -77.3573, \"timestamp\": 1670520850.9, \"vertical_velocity\": -0.32512, \"track\": 181.3, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439386, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520852, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520851.1, \"horizontal_velocity\": 137.92265802933335, \"object_id\": \"a1e946\", \"latitude\": 38.884479, \"longitude\": -77.3573, \"timestamp\": 1670520851.1, \"vertical_velocity\": -0.32512, \"track\": 181.3, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439388, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520853, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520853.0, \"horizontal_velocity\": 137.92265802933335, \"object_id\": \"a1e946\", \"latitude\": 38.884479, \"longitude\": -77.3573, \"timestamp\": 1670520853.0, \"vertical_velocity\": 0.0, \"track\": 181.3, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439389, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520854, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520853.9, \"horizontal_velocity\": 137.40821320266667, \"object_id\": \"a1e946\", \"latitude\": 38.880798, \"longitude\": -77.357423, \"timestamp\": 1670520853.9, \"vertical_velocity\": 0.32512, \"track\": 181.3, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439390, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520855, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520854.7, \"horizontal_velocity\": 137.40821320266667, \"object_id\": \"a1e946\", \"latitude\": 38.880295, \"longitude\": -77.357423, \"timestamp\": 1670520854.7, \"vertical_velocity\": 0.65024, \"track\": 181.3, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439390, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520856, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520855.3, \"horizontal_velocity\": 137.40821320266667, \"object_id\": \"a1e946\", \"latitude\": 38.880295, \"longitude\": -77.357423, \"timestamp\": 1670520855.3, \"vertical_velocity\": 0.65024, \"track\": 181.3, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439393, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520859, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520858.3, \"horizontal_velocity\": 137.40821320266667, \"object_id\": \"a1e946\", \"latitude\": 38.880295, \"longitude\": -77.357423, \"timestamp\": 1670520858.3, \"vertical_velocity\": 0.65024, \"track\": 181.3, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439395, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520860, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520860.0, \"horizontal_velocity\": 137.40821320266667, \"object_id\": \"a1e946\", \"latitude\": 38.873383, \"longitude\": -77.357602, \"timestamp\": 1670520860.0, \"vertical_velocity\": 0.0, \"track\": 181.3, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439396, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520861, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520860.5, \"horizontal_velocity\": 137.40821320266667, \"object_id\": \"a1e946\", \"latitude\": 38.872787, \"longitude\": -77.357602, \"timestamp\": 1670520860.5, \"vertical_velocity\": 0.0, \"track\": 181.3, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439401, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520866, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520866.0, \"horizontal_velocity\": 136.89376837600003, \"object_id\": \"a1e946\", \"latitude\": 38.865372, \"longitude\": -77.357841, \"timestamp\": 1670520866.0, \"vertical_velocity\": -0.65024, \"track\": 181.3, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439402, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520867, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520866.9, \"horizontal_velocity\": 136.89376837600003, \"object_id\": \"a1e946\", \"latitude\": 38.864136, \"longitude\": -77.357849, \"timestamp\": 1670520866.9, \"vertical_velocity\": -0.32512, \"track\": 181.3, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439403, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520868, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520867.6, \"horizontal_velocity\": 136.89376837600003, \"object_id\": \"a1e946\", \"latitude\": 38.863495, \"longitude\": -77.357901, \"timestamp\": 1670520867.6, \"vertical_velocity\": -0.32512, \"track\": 181.3, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439405, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520869, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520869.0, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.863495, \"longitude\": -77.357901, \"timestamp\": 1670520869.0, \"vertical_velocity\": -0.32512, \"track\": 181.1, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439409, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520873, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520872.8, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.863495, \"longitude\": -77.357901, \"timestamp\": 1670520872.8, \"vertical_velocity\": -0.32512, \"track\": 181.1, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439409, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520874, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520873.5, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.863495, \"longitude\": -77.357901, \"timestamp\": 1670520873.5, \"vertical_velocity\": -0.32512, \"track\": 181.1, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439419, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520883, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520882.8, \"horizontal_velocity\": 136.84232389333334, \"object_id\": \"a1e946\", \"latitude\": 38.863495, \"longitude\": -77.357901, \"timestamp\": 1670520882.8, \"vertical_velocity\": -0.32512, \"track\": 181.1, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439420, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520884, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520883.6, \"horizontal_velocity\": 131.1834308, \"object_id\": \"a1e946\", \"latitude\": 38.863495, \"longitude\": -77.357901, \"timestamp\": 1670520883.6, \"vertical_velocity\": 0.0, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439421, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520885, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520885.0, \"horizontal_velocity\": 131.1834308, \"object_id\": \"a1e946\", \"latitude\": 38.842667, \"longitude\": -77.358379, \"timestamp\": 1670520885.0, \"vertical_velocity\": 0.0, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439422, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520886, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520885.4, \"horizontal_velocity\": 131.1834308, \"object_id\": \"a1e946\", \"latitude\": 38.841977, \"longitude\": -77.358398, \"timestamp\": 1670520885.4, \"vertical_velocity\": 0.0, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439422, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520887, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520886.2, \"horizontal_velocity\": 131.1834308, \"object_id\": \"a1e946\", \"latitude\": 38.841977, \"longitude\": -77.358398, \"timestamp\": 1670520886.2, \"vertical_velocity\": 0.0, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439424, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520888, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520887.9, \"horizontal_velocity\": 131.1834308, \"object_id\": \"a1e946\", \"latitude\": 38.839184, \"longitude\": -77.358459, \"timestamp\": 1670520887.9, \"vertical_velocity\": 0.0, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439425, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520889, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520888.8, \"horizontal_velocity\": 131.1834308, \"object_id\": \"a1e946\", \"latitude\": 38.837906, \"longitude\": -77.358438, \"timestamp\": 1670520888.8, \"vertical_velocity\": 0.0, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439426, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520890, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520889.9, \"horizontal_velocity\": 127.58231701333334, \"object_id\": \"a1e946\", \"latitude\": 38.836624, \"longitude\": -77.358498, \"timestamp\": 1670520889.9, \"vertical_velocity\": 0.0, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439427, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520891, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520890.8, \"horizontal_velocity\": 126.55342736, \"object_id\": \"a1e946\", \"latitude\": 38.835972, \"longitude\": -77.358521, \"timestamp\": 1670520890.8, \"vertical_velocity\": -0.32512, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439442, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520905, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520904.8, \"horizontal_velocity\": 126.55342736, \"object_id\": \"a1e946\", \"latitude\": 38.835297, \"longitude\": -77.358498, \"timestamp\": 1670520904.8, \"vertical_velocity\": -0.32512, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439443, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520906, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520905.8, \"horizontal_velocity\": 126.55342736, \"object_id\": \"a1e946\", \"latitude\": 38.835297, \"longitude\": -77.358498, \"timestamp\": 1670520905.8, \"vertical_velocity\": -0.32512, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439444, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520907, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520906.8, \"horizontal_velocity\": 126.55342736, \"object_id\": \"a1e946\", \"latitude\": 38.835297, \"longitude\": -77.358498, \"timestamp\": 1670520906.8, \"vertical_velocity\": -0.32512, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439445, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520908, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520907.7, \"horizontal_velocity\": 126.55342736, \"object_id\": \"a1e946\", \"latitude\": 38.81781, \"longitude\": -77.358916, \"timestamp\": 1670520907.7, \"vertical_velocity\": -0.32512, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439447, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520910, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520909.6, \"horizontal_velocity\": 126.55342736, \"object_id\": \"a1e946\", \"latitude\": 38.81781, \"longitude\": -77.358916, \"timestamp\": 1670520909.6, \"vertical_velocity\": -0.32512, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{ + "topic": "object", + "message_content": { + "PushTimestamp": 1693439455, + "DeviceType": "TBC", + "ID": "TBC", + "DeploymentID": "TBC", + "CurrentLocation": "TBC", + "Status": "Debug", + "MessageType": "Event", + "ModelVersion": "null", + "FirmwareVersion": "v0.0.0", + "Selected Object": "{\"ts_entry\": 1670520918, \"altitude\": 868.6800000000001, \"ts_altitude\": 1670520917.3, \"horizontal_velocity\": 126.55342736, \"object_id\": \"a1e946\", \"latitude\": 38.81781, \"longitude\": -77.358916, \"timestamp\": 1670520917.3, \"vertical_velocity\": -0.32512, \"track\": 180.9, \"object_type\": \"aircraft\"}" + }, + "delay": 0.0 +}, +{} +] diff --git a/tests/test-harness.env b/tests/test-harness.env new file mode 100644 index 0000000..55c1449 --- /dev/null +++ b/tests/test-harness.env @@ -0,0 +1,7 @@ +# .env file for testing yolo-detect +INPUT_FILE_PATH=/test-data/test-input.json +SUBSCRIPTION_TOPICS=capture,logger +CONFIG_JSON_TOPIC=test-harness-config +OUTPUT_FILE_PATH=/test-data/test-output.json +SHUTDOWN_AFTER=5 +MQTT_IP=mqtt \ No newline at end of file diff --git a/tests/test.sh b/tests/test.sh new file mode 100644 index 0000000..46be988 --- /dev/null +++ b/tests/test.sh @@ -0,0 +1,15 @@ +#!/bin/sh +PURPLE='\033[1;35m' +NC='\033[0m' # No Color + +echo -e "${PURPLE}Set up integration tests${NC}" +docker-compose build axis-ptz-controller + +echo -e "${PURPLE}Bring up containers${NC}" +docker-compose up --abort-on-container-exit + +echo -e "${PURPLE}Run integration tests${NC}" +pytest test_integration.py + +echo -e "${PURPLE}Tear down integration tests${NC}" +rm test-data/test-output.json diff --git a/tests/test_integration.py b/tests/test_integration.py new file mode 100644 index 0000000..767b01b --- /dev/null +++ b/tests/test_integration.py @@ -0,0 +1,70 @@ +import json +import pandas as pd +from matplotlib import pyplot as plt + + +def test_integration() -> None: + """ + Tests integration of the message handler with the MQTT client. Designed to be run after docker-compose.test.yaml. + Recommended to run from test.sh. + """ + + with open("./test-data/test-output.json") as output_file: + output = json.load(output_file) + + ts = pd.DataFrame.from_dict([json.loads(d['message_content']['Logger'])['camera-pointing'] for d in output[:-1]]) + + plot_time_series(ts) + + +def plot_time_series(ts: pd.DataFrame) -> None: + """Plot time series produced by processing messages. + + Parameters + ---------- + ts : pd.DataFrame() + Dataframe containing time series to plot + + Returns + ------- + None + """ + + # Plot pan angle + fig, axs = plt.subplots(2, 2, figsize=[12.8, 9.6]) + axs[0, 0].plot(ts["timestamp_c"], ts["rho_c"] - ts["rho_o"], label="error") + axs[0, 0].plot(ts["timestamp_c"], ts["rho_c"], label="camera") + axs[0, 0].plot(ts["timestamp_c"], ts["rho_o"], label="object") + axs[0, 0].legend() + axs[0, 0].set_title("Camera and Object Pan Angle and Difference") + axs[0, 0].set_xlabel("Timestamp [s]") + axs[0, 0].set_ylabel("Pan Angle [deg]") + + # Plot tilt angle + axs[1, 0].plot(ts["timestamp_c"], ts["tau_c"] - ts["tau_o"], label="error") + axs[1, 0].plot(ts["timestamp_c"], ts["tau_c"], label="camera") + axs[1, 0].plot(ts["timestamp_c"], ts["tau_o"], label="object") + axs[1, 0].legend() + axs[1, 0].set_title("Camera and Object Tilt Angle and Difference") + axs[1, 0].set_xlabel("Timestamp [s]") + axs[1, 0].set_ylabel("Tilt Angle [deg]") + + # Plot pan angular rate angle + axs[0, 1].plot(ts["timestamp_c"], ts["rho_dot_c"] - ts["rho_dot_o"], label="error") + axs[0, 1].plot(ts["timestamp_c"], ts["rho_dot_c"], label="camera") + axs[0, 1].plot(ts["timestamp_c"], ts["rho_dot_o"], label="object") + axs[0, 1].legend() + axs[0, 1].set_title("Camera and Object Pan Angular Rate and Difference") + axs[0, 0].set_xlabel("Timestamp [s]") + axs[0, 1].set_ylabel("Pan Anglular Rate [deg/s]") + + # Plot tilt angular rate angle + axs[1, 1].plot(ts["timestamp_c"], ts["tau_dot_c"] - ts["tau_dot_o"], label="error") + axs[1, 1].plot(ts["timestamp_c"], ts["tau_dot_c"], label="camera") + axs[1, 1].plot(ts["timestamp_c"], ts["tau_dot_o"], label="object") + axs[1, 1].legend() + axs[1, 1].set_title("Camera and Object Tilt Angular Rate and Difference") + axs[0, 0].set_xlabel("Timestamp [s]") + axs[1, 1].set_ylabel("Tilt Anglular Rate [deg/s]") + + plt.show() From bde3dcc1b3429c999de8a16af071700277f1c717 Mon Sep 17 00:00:00 2001 From: spearw Date: Thu, 31 Aug 2023 14:18:50 -0400 Subject: [PATCH 26/56] Commit .env file --- tests/axis-ptz-controller.env | 35 +++++++++++++++++++++++++++++++++++ tests/docker-compose.yaml | 4 ++++ tests/test.sh | 1 + 3 files changed, 40 insertions(+) create mode 100644 tests/axis-ptz-controller.env diff --git a/tests/axis-ptz-controller.env b/tests/axis-ptz-controller.env new file mode 100644 index 0000000..714718d --- /dev/null +++ b/tests/axis-ptz-controller.env @@ -0,0 +1,35 @@ +MQTT_IP=mqtt +CAMERA_IP=CAMERA_IP +CAMERA_USER=CAMERA_USERNAME +CAMERA_PASSWORD=CAMERA_PASSWORD +CONFIG_TOPIC=axis-config +ORIENTATION_TOPIC=orientation +OBJECT_TOPIC=object +CAPTURE_TOPIC=capture +LOGGER_TOPIC=logger +HEARTBEAT_INTERVAL=10 +# Sourced from a local .bashrc file for security +# TRIPOD_LATITUDE= +# TRIPOD_LONGITUDE= +# TRIPOD_ALTITUDE= +UPDATE_INTERVAL=0.1 +CAPTURE_INTERVAL=2 +CAPTURE_DIR=/data/tosort +LEAD_TIME=0.0 +PAN_GAIN=0.1 +PAN_RATE_MIN=1.0 +PAN_RATE_MAX=100.0 +TILT_GAIN=0.4 +TILT_RATE_MIN=1.0 +TILT_RATE_MAX=100.0 +FOCUS_SLOPE=0.0006 +FOCUS_INTERCEPT=54 +JPEG_RESOLUTION=1920x1080 +JPEG_COMPRESSION=5 +USE_MQTT=True +USE_CAMERA=False +AUTO_FOCUS=False +INCLUDE_AGE=False +LOG_TO_MQTT=True +CONTINUE_ON_EXCEPTION=False +LOG_LEVEL=DEBUG diff --git a/tests/docker-compose.yaml b/tests/docker-compose.yaml index a6e4342..7984161 100644 --- a/tests/docker-compose.yaml +++ b/tests/docker-compose.yaml @@ -32,5 +32,9 @@ services: - mqtt env_file: - axis-ptz-controller.env + environment: + - TRIPOD_LATITUDE=${TRIPOD_LATITUDE} + - TRIPOD_LONGITUDE=${TRIPOD_LONGITUDE} + - TRIPOD_ALTITUDE=${TRIPOD_ALTITUDE} volumes: - ./test-data/tosort:/data/tosort \ No newline at end of file diff --git a/tests/test.sh b/tests/test.sh index 46be988..d1bc512 100644 --- a/tests/test.sh +++ b/tests/test.sh @@ -3,6 +3,7 @@ PURPLE='\033[1;35m' NC='\033[0m' # No Color echo -e "${PURPLE}Set up integration tests${NC}" +source .bashrc docker-compose build axis-ptz-controller echo -e "${PURPLE}Bring up containers${NC}" From 348c579435b02f58a5c70531a1515cfaeb650300 Mon Sep 17 00:00:00 2001 From: spearw Date: Thu, 31 Aug 2023 14:27:04 -0400 Subject: [PATCH 27/56] Black formatting --- tests/test_integration.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_integration.py b/tests/test_integration.py index 767b01b..9fdc7d9 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -12,7 +12,12 @@ def test_integration() -> None: with open("./test-data/test-output.json") as output_file: output = json.load(output_file) - ts = pd.DataFrame.from_dict([json.loads(d['message_content']['Logger'])['camera-pointing'] for d in output[:-1]]) + ts = pd.DataFrame.from_dict( + [ + json.loads(d["message_content"]["Logger"])["camera-pointing"] + for d in output[:-1] + ] + ) plot_time_series(ts) From 97372c5c231cb4bdeeb191aacdf8f3b7842a59db Mon Sep 17 00:00:00 2001 From: spearw Date: Thu, 31 Aug 2023 14:29:31 -0400 Subject: [PATCH 28/56] Remove old tests --- axis-ptz-controller/test_integration.py | 391 ------------------------ axis-ptz-controller/test_messages.py | 263 ---------------- 2 files changed, 654 deletions(-) delete mode 100644 axis-ptz-controller/test_integration.py delete mode 100644 axis-ptz-controller/test_messages.py diff --git a/axis-ptz-controller/test_integration.py b/axis-ptz-controller/test_integration.py deleted file mode 100644 index d5098b2..0000000 --- a/axis-ptz-controller/test_integration.py +++ /dev/null @@ -1,391 +0,0 @@ -from argparse import ArgumentParser -from datetime import datetime -import json -import logging -import os -import time -from typing import Any, Dict - -import coloredlogs -from matplotlib import pyplot as plt -import pandas as pd - -from axis_ptz_controller import AxisPtzController - -STYLES = { - "critical": {"bold": True, "color": "red"}, - "debug": {"color": "green"}, - "error": {"color": "red"}, - "info": {"color": "white"}, - "notice": {"color": "magenta"}, - "spam": {"color": "green", "faint": True}, - "success": {"bold": True, "color": "green"}, - "verbose": {"color": "blue"}, - "warning": {"color": "yellow"}, -} -coloredlogs.install( - level=logging.INFO, - fmt="%(asctime)s.%(msecs)03d \033[0;90m%(levelname)-8s " - "" - "\033[0;36m%(filename)-18s%(lineno)3d\033[00m " - "%(message)s", - level_styles=STYLES, -) - -HEARTBEAT_INTERVAL = 10 -LOOP_INTERVAL = 0.01 -CAPTURE_INTERVAL = 2 -LEAD_TIME = 0.0 -PAN_GAIN = 0.1 -PAN_RATE_MIN = 1.0 -PAN_RATE_MAX = 100.0 -TILT_GAIN = 0.4 -TILT_RATE_MIN = 1.0 -TILT_RATE_MAX = 100.0 -JPEG_RESOLUTION = "1920x1080" -JPEG_COMPRESSION = 5 - - -def make_controller(use_mqtt: bool) -> AxisPtzController: - """Construct a controller. - - Note that if use_mqtt = True then an MQTT broker must be started - manually. - - Parameters - ---------- - use_mqtt: bool - Flag to use MQTT, or not - - Returns - ------- - None - """ - controller = AxisPtzController( - hostname=os.environ.get("HOSTNAME", ""), - camera_ip=os.getenv("CAMERA_IP", ""), - camera_user=os.getenv("CAMERA_USER", ""), - camera_password=os.getenv("CAMERA_PASSWORD", ""), - mqtt_ip=os.getenv("MQTT_IP", ""), - config_topic=os.getenv("CONFIG_TOPIC", ""), - orientation_topic=os.getenv("ORIENTATION_TOPIC", ""), - object_topic=os.getenv("OBJECT_TOPIC", ""), - image_filename_topic=os.getenv("IMAGE_FILENAME_TOPIC", ""), - capture_topic=os.getenv("CAPTURE_TOPIC", ""), - logger_topic=os.getenv("LOGGER_TOPIC", ""), - heartbeat_interval=HEARTBEAT_INTERVAL, - loop_interval=LOOP_INTERVAL, - capture_interval=CAPTURE_INTERVAL, - lead_time=LEAD_TIME, - pan_gain=PAN_GAIN, - pan_rate_min=PAN_RATE_MIN, - pan_rate_max=PAN_RATE_MAX, - tilt_gain=TILT_GAIN, - tilt_rate_min=TILT_RATE_MIN, - tilt_rate_max=TILT_RATE_MAX, - jpeg_resolution=JPEG_RESOLUTION, - jpeg_compression=JPEG_COMPRESSION, - use_mqtt=use_mqtt, - use_camera=False, - include_age=False, - ) - return controller - - -def get_config_msg(controller: AxisPtzController) -> str: - """Populate a config message, reading actual, by private longitude - and latitude from the environment. - - Parameters - ---------- - None - - Returns - ------- - msg : str - The configuration message - """ - data: Dict[str, Any] = {} - data["axis-ptz-controller"] = {} - data["axis-ptz-controller"]["tripod_longitude"] = float( - os.getenv("TRIPOD_LONGITUDE", "-77.0") - ) # [deg] - data["axis-ptz-controller"]["tripod_latitude"] = float( - os.getenv("TRIPOD_LATITUDE", "38.0") - ) # [deg] - data["axis-ptz-controller"]["tripod_altitude"] = 86.46 # [m] - msg = controller.generate_payload_json( - push_timestamp=int(datetime.utcnow().timestamp()), - device_type="TBC", - id_="TBC", - deployment_id="TBC", - current_location="TBC", - status="Debug", - message_type="Event", - model_version="null", - firmware_version="v0.0.0", - data_payload_type="Configuration", - data_payload=json.dumps(data), - ) - return msg - - -def get_orientation_msg(controller: AxisPtzController) -> str: - """Populate an orientation message with all 0 deg angles. - - Parameters - ---------- - None - - Returns - ------- - msg : str - The orientation message - """ - with open("data/orientation_msg_data_0s.json", "r") as f: - data = json.load(f) - msg = controller.generate_payload_json( - push_timestamp=int(datetime.utcnow().timestamp()), - device_type="TBC", - id_="TBC", - deployment_id="TBC", - current_location="TBC", - status="Debug", - message_type="Event", - model_version="null", - firmware_version="v0.0.0", - data_payload_type="Orientation", - data_payload=json.dumps(data), - ) - return msg - - -def make_object_msg( - controller: AxisPtzController, track: pd.DataFrame, index: int -) -> str: - """Populate a object message with track data at the specified - index. - - Parameters - ---------- - track : pd.DataFrame() - The track data - index : int - The index of the track data to use - - Returns - ------- - msg : str - The object message - """ - data = track.iloc[index, :].to_dict() - msg = controller.generate_payload_json( - push_timestamp=int(datetime.utcnow().timestamp()), - device_type="TBC", - id_="TBC", - deployment_id="TBC", - current_location="TBC", - status="Debug", - message_type="Event", - model_version="null", - firmware_version="v0.0.0", - data_payload_type="Selected Object", - data_payload=json.dumps(data), - ) - return msg - - -def read_track_data(track_id: str) -> pd.DataFrame: - """Read a track file and convert to standard units of measure. - - Parameters - ---------- - track_id : str - The track identifier - - Returns - ------- - track : pd.DataFrame() - The track data read from the file - """ - track = pd.read_csv(f"data/{track_id}-processed-track.csv") - track["altitude"] *= 0.3048 # [ft] * [m/ft] = [m] - track["horizontal_velocity"] *= ( - 6076.12 / 3600 * 0.3048 - ) # [nm/h] * [ft/nm] / [s/h] * [m/ft] = [m/s] - track["vertical_velocity"] *= 0.3048 / 60 # [ft/s] * [m/ft] / [s/m] = [m/s] - return track - - -def plot_time_series(ts: pd.DataFrame) -> None: - """Plot time series produced by processing messages. - - Parameters - ---------- - ts : pd.DataFrame() - Dataframe containing time series to plot - - Returns - ------- - None - """ - - # Plot pan angle - fig, axs = plt.subplots(2, 2, figsize=[12.8, 9.6]) - axs[0, 0].plot(ts["timestamp_c"], ts["rho_c"] - ts["rho_o"], label="error") - axs[0, 0].plot(ts["timestamp_c"], ts["rho_c"], label="camera") - axs[0, 0].plot(ts["timestamp_c"], ts["rho_o"], label="object") - axs[0, 0].legend() - axs[0, 0].set_title("Camera and Object Pan Angle and Difference") - axs[0, 0].set_xlabel("Timestamp [s]") - axs[0, 0].set_ylabel("Pan Angle [deg]") - - # Plot tilt angle - axs[1, 0].plot(ts["timestamp_c"], ts["tau_c"] - ts["tau_o"], label="error") - axs[1, 0].plot(ts["timestamp_c"], ts["tau_c"], label="camera") - axs[1, 0].plot(ts["timestamp_c"], ts["tau_o"], label="object") - axs[1, 0].legend() - axs[1, 0].set_title("Camera and Object Tilt Angle and Difference") - axs[1, 0].set_xlabel("Timestamp [s]") - axs[1, 0].set_ylabel("Tilt Angle [deg]") - - # Plot pan angular rate angle - axs[0, 1].plot(ts["timestamp_c"], ts["rho_dot_c"] - ts["rho_dot_o"], label="error") - axs[0, 1].plot(ts["timestamp_c"], ts["rho_dot_c"], label="camera") - axs[0, 1].plot(ts["timestamp_c"], ts["rho_dot_o"], label="object") - axs[0, 1].legend() - axs[0, 1].set_title("Camera and Object Pan Angular Rate and Difference") - axs[0, 0].set_xlabel("Timestamp [s]") - axs[0, 1].set_ylabel("Pan Anglular Rate [deg/s]") - - # Plot tilt angular rate angle - axs[1, 1].plot(ts["timestamp_c"], ts["tau_dot_c"] - ts["tau_dot_o"], label="error") - axs[1, 1].plot(ts["timestamp_c"], ts["tau_dot_c"], label="camera") - axs[1, 1].plot(ts["timestamp_c"], ts["tau_dot_o"], label="object") - axs[1, 1].legend() - axs[1, 1].set_title("Camera and Object Tilt Angular Rate and Difference") - axs[0, 0].set_xlabel("Timestamp [s]") - axs[1, 1].set_ylabel("Tilt Anglular Rate [deg/s]") - - plt.show() - - -def main() -> None: - """Read a track file and process the corresponding messages using - MQTT, or not. - - Parameters - ---------- - None - - Returns - ------- - None - """ - # Provide for some command line arguments - parser = ArgumentParser( - description="Read a track file and process the corresponding messages" - ) - parser.add_argument( - "-t", - "--track-id", - default="A1E946", - help="The track identifier to process: A1E946 (the default) or A19A08", - ) - parser.add_argument( - "-m", - "--use-mqtt", - action="store_true", - help="Use MQTT to process messages", - ) - args = parser.parse_args() - - # Read the track data - logging.info(f"Reading track for id: {args.track_id}") - track = read_track_data(args.track_id) - - # Make the controller, subscribe to all topics, and publish, or - # process, one message to each topic - logging.info("Making the controller, and subscribing to topics") - controller = make_controller(args.use_mqtt) - controller.add_subscribe_topic(controller.config_topic, controller._config_callback) - controller.add_subscribe_topic( - controller.orientation_topic, controller._orientation_callback - ) - controller.add_subscribe_topic(controller.object_topic, controller._object_callback) - config_msg = get_config_msg(controller) - orientation_msg = get_orientation_msg(controller) - index = 0 - object_msg = make_object_msg(controller, track, index) - if controller.use_mqtt: - logging.info(f"Publishing config msg: {config_msg}") - controller.publish_to_topic(controller.config_topic, config_msg) - time.sleep(LOOP_INTERVAL) - - logging.info(f"Publishing orientation msg: {orientation_msg}") - controller.publish_to_topic(controller.orientation_topic, orientation_msg) - time.sleep(LOOP_INTERVAL) - - logging.info(f"Publishing object msg: {object_msg}") - controller.publish_to_topic(controller.object_topic, object_msg) - time.sleep(LOOP_INTERVAL) - - else: - _client = None - _userdata = None - controller._config_callback(_client, _userdata, config_msg) - controller._orientation_callback(_client, _userdata, orientation_msg) - controller._object_callback(_client, _userdata, object_msg) - - # Initialize history for plotting - history = {} - data = controller.decode_payload(object_msg, "Selected Object") - history["timestamp_c"] = [data["timestamp"]] - history["rho_o"] = [controller.rho_o] - history["tau_o"] = [controller.tau_o] - history["rho_dot_o"] = [controller.rho_dot_o] - history["tau_dot_o"] = [controller.tau_dot_o] - history["rho_c"] = [controller.rho_c] - history["tau_c"] = [controller.tau_c] - history["rho_dot_c"] = [controller.rho_dot_c] - history["tau_dot_c"] = [controller.tau_dot_c] - - # Loop in camera time - dt_c = controller.loop_interval - timestamp_c = history["timestamp_c"][0] - while index < track.shape[0] - 1: - timestamp_c += dt_c - - # Process each object message when received - if timestamp_c >= track["timestamp"][index + 1]: - index = track["timestamp"][timestamp_c >= track["timestamp"]].index[-1] - object_msg = make_object_msg(controller, track, index) - if controller.use_mqtt: - logging.info(f"Publishing object msg: {object_msg}") - controller.publish_to_topic(controller.object_topic, object_msg) - time.sleep(LOOP_INTERVAL) - - else: - controller._object_callback(_client, _userdata, object_msg) - - # Always update pointing - controller._update_pointing() - - # Append to history for plotting - history["timestamp_c"].append(timestamp_c) - history["rho_o"].append(controller.rho_o) - history["tau_o"].append(controller.tau_o) - history["rho_dot_o"].append(controller.rho_dot_o) - history["tau_dot_o"].append(controller.tau_dot_o) - history["rho_c"].append(controller.rho_c) - history["tau_c"].append(controller.tau_c) - history["rho_dot_c"].append(controller.rho_dot_c) - history["tau_dot_c"].append(controller.tau_dot_c) - - # Convert history dictionary to a dataframe, and plot - ts = pd.DataFrame.from_dict(history) - plot_time_series(ts) - - -if __name__ == "__main__": - main() diff --git a/axis-ptz-controller/test_messages.py b/axis-ptz-controller/test_messages.py deleted file mode 100644 index 9504f4e..0000000 --- a/axis-ptz-controller/test_messages.py +++ /dev/null @@ -1,263 +0,0 @@ -from argparse import ArgumentParser -from datetime import datetime -import json -import logging -import os -import time -from typing import Any, Dict, Union - -import paho.mqtt.client as mqtt -import pandas as pd - -from base_mqtt_pub_sub import BaseMQTTPubSub -from test_integration import ( - get_config_msg, - get_orientation_msg, - make_controller, - make_object_msg, - plot_time_series, - read_track_data, -) - -LOOP_INTERVAL = 0.1 - - -class MessageHandler(BaseMQTTPubSub): - """Subscribe to all required topics, and open files for logging.""" - - def __init__( - self, - config_topic: str, - orientation_topic: str, - object_topic: str, - logger_topic: str, - **kwargs: Any, - ): - """Initialize a MessageHandler by subscribing to all required - topics, connecting to the MQTT broker, and opening logging - files. - - Parameters - ---------- - config_topic: str - MQTT topic for publishing or subscribing to configuration - messages - orientation_topic: str - MQTT topic for publishing or subscribing to orientation - messages - object_topic: str - MQTT topic for publishing or subscribing to object - messages - logger_topic: str - MQTT topic for publishing or subscribing to logger - messages - - Returns - ------- - MessageHandler - """ - # Parent class handles kwargs, including MQTT IP - super().__init__(**kwargs) - self.config_topic = config_topic - self.orientation_topic = orientation_topic - self.object_topic = object_topic - self.logger_topic = logger_topic - - # Connect MQTT client - logging.info("Connecting MQTT client") - self.connect_client() - time.sleep(5) - self.publish_registration("Message Handler Module Registration") - - # Open files for logging - self.camera_pointing_filename = "camera-pointing.csv" - self.camera_pointing_file = open(self.camera_pointing_filename, "w") - self.camera_pointing_keys = [ - "timestamp_c", - "rho_o", - "tau_o", - "rho_dot_o", - "tau_dot_o", - "rho_c", - "tau_c", - "rho_dot_c", - "tau_dot_c", - ] - self.camera_pointing_file.write(",".join(self.camera_pointing_keys) + "\n") - - def decode_payload( - self, msg: Union[mqtt.MQTTMessage, str], data_payload_type: str - ) -> Dict[Any, Any]: - """ - Decode the payload carried by a message. - - Parameters - ---------- - payload: mqtt.MQTTMessage - The MQTT message - - Returns - ------- - data : Dict[Any, Any] - The data payload of the message payload - """ - if type(msg) == mqtt.MQTTMessage: - payload = msg.payload.decode() - else: - payload = msg - data_payload = json.loads(payload)[data_payload_type] - return json.loads(data_payload) - - def _logger_callback( - self, _client: mqtt.Client, _userdata: Dict[Any, Any], msg: mqtt.MQTTMessage - ) -> None: - """ - Process logging message based on keys. - - Parameters - ---------- - _client: mqtt.Client - MQTT client - _userdata: dict - Any required user data - msg: Any - A JSON string with {timestamp: ____, data: ____,} - - Returns - ------- - None - """ - data = self.decode_payload(msg, "Logger") - if "camera-pointing" in data: - logging.info(data["camera-pointing"]) - self.camera_pointing_file.write( - ",".join( - [str(data["camera-pointing"][k]) for k in self.camera_pointing_keys] - ) - + "\n" - ) - self.camera_pointing_file.flush() - elif "info" in data: - logging.info(data["info"]["message"]) - - -def make_handler() -> MessageHandler: - """Construct a MessageHandler. - - Note that an MQTT broker must be started manually. - - Parameters - ---------- - None - - Returns - ------- - handler: MessageHandler - The message handler - """ - handler = MessageHandler( - mqtt_ip=os.getenv("MQTT_IP", ""), - config_topic=os.getenv("CONFIG_TOPIC", ""), - orientation_topic=os.getenv("ORIENTATION_TOPIC", ""), - object_topic=os.getenv("OBJECT_TOPIC", ""), - logger_topic=os.getenv("LOGGER_TOPIC", ""), - ) - return handler - - -def main() -> None: - """Read a track file and publish the corresponding messages using - MQTT. Subscribe to the logger topic and process log messages to - collect camera pointing time series for plotting. - - Parameters - ---------- - None - - Returns - ------- - None - """ - - # Provide for some command line arguments - parser = ArgumentParser( - description="Read a track file and process the corresponding messages" - ) - parser.add_argument( - "-t", - "--track-id", - default="A1E946", - help="The track identifier to process: A1E946 (the default) or A19A08", - ) - args = parser.parse_args() - - # Read the track data - logging.info(f"Reading track for id: {args.track_id}") - track = read_track_data(args.track_id) - - # Make the handler, and subscribe to the logger topic - logging.info("Making the handler, and subscribing to topics") - handler = make_handler() - handler.add_subscribe_topic(handler.logger_topic, handler._logger_callback) - data = { - "info": { - "message": "Subscribed to the logger", - } - } - logger_msg = handler.generate_payload_json( - push_timestamp=int(datetime.utcnow().timestamp()), - device_type="TBC", - id_="TBC", - deployment_id="TBC", - current_location="TBC", - status="Debug", - message_type="Event", - model_version="null", - firmware_version="v0.0.0", - data_payload_type="Logger", - data_payload=json.dumps(data), - ) - handler.publish_to_topic(handler.logger_topic, logger_msg) - - # Publish the configuration and orientation message, and the first - # object message - controller = make_controller(True) - config_msg = get_config_msg(controller) - orientation_msg = get_orientation_msg(controller) - index = 0 - object_msg = make_object_msg(controller, track, index) - logging.info(f"Publishing config msg: {config_msg}") - handler.publish_to_topic(handler.config_topic, config_msg) - time.sleep(LOOP_INTERVAL) - - logging.info(f"Publishing orientation msg: {orientation_msg}") - handler.publish_to_topic(handler.orientation_topic, orientation_msg) - time.sleep(LOOP_INTERVAL) - - logging.info(f"Publishing object msg: {object_msg}") - handler.publish_to_topic(handler.object_topic, object_msg) - time.sleep(LOOP_INTERVAL) - - # Loop in camera time - dt_c = LOOP_INTERVAL - timestamp_c = track["timestamp"][index] - while index < track.shape[0] - 1: - time.sleep(LOOP_INTERVAL) - timestamp_c += dt_c - - # Process each object message when received - if timestamp_c >= track["timestamp"][index + 1]: - index = track["timestamp"][timestamp_c >= track["timestamp"]].index[-1] - object_msg = make_object_msg(controller, track, index) - logging.info(f"Publishing object msg: {object_msg}") - handler.publish_to_topic(handler.object_topic, object_msg) - - # Read camera pointing file as a dataframe, and plot - time.sleep(5) - handler.camera_pointing_file.close() - ts = pd.read_csv(handler.camera_pointing_filename) - plot_time_series(ts) - - -if __name__ == "__main__": - main() From d26e5c1df292122cbaf32c744baafb86c2b4a95c Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Thu, 22 Feb 2024 15:15:23 -0500 Subject: [PATCH 29/56] Updated ENV --- axis-ptz-controller.env | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/axis-ptz-controller.env b/axis-ptz-controller.env index 6e222ae..4ff11db 100644 --- a/axis-ptz-controller.env +++ b/axis-ptz-controller.env @@ -1,13 +1,15 @@ +PROJECT_NAME= +HOSTNAME= MQTT_IP=mqtt CAMERA_IP= CAMERA_USER= CAMERA_PASSWORD= -CONFIG_TOPIC=/Multimodal/${HOSTNAME}/config/shipscan-c2/JSON -ORIENTATION_TOPIC=/Multimodal/${HOSTNAME}/orientation/edgetech-auto-orienter/JSON -OBJECT_TOPIC=/Multimodal/${HOSTNAME}/object/edgetech-object-ledger/JSON -IMAGE_FILENAME_TOPIC=/Multimodal/${HOSTNAME}/imageFilename/edgetech-axis-ptz-controller/JSON -CAPTURE_TOPIC=/Multimodal/${HOSTNAME}/capture/edgetech-axis-ptz-controller/JSON -LOGGER_TOPIC=/Multimodal/${HOSTNAME}/logger/edgetech-axis-ptz-controller/JSON +CONFIG_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/config/shipscan-c2/JSON +ORIENTATION_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/orientation/edgetech-auto-orienter/JSON +OBJECT_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/object/edgetech-object-ledger/JSON +IMAGE_FILENAME_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/imageFilename/edgetech-axis-ptz-controller/JSON +CAPTURE_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/capture/edgetech-axis-ptz-controller/JSON +LOGGER_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/logger/edgetech-axis-ptz-controller/JSON HEARTBEAT_INTERVAL=10 TRIPOD_LATITUDE= TRIPOD_LONGITUDE= From 5974783c1bedb43f00418461f1080b5925abe174 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Fri, 1 Mar 2024 14:13:23 -0500 Subject: [PATCH 30/56] Merge rl/make-auto-focus-option and blacken --- axis-ptz-controller/axis_ptz_controller.py | 30 +++++++++++---------- axis-ptz-controller/axis_ptz_utilities.py | 1 - axis-ptz-controller/camera_configuration.py | 2 -- axis-ptz-controller/camera_control.py | 5 +--- axis-ptz-controller/test_integration.py | 1 - axis-ptz-controller/test_modules.py | 6 +++-- 6 files changed, 21 insertions(+), 24 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 608ab4d..40a4376 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -376,7 +376,9 @@ def __init__( f"Absolute move to pan: {self.rho_c}, and tilt: {self.tau_c}, with zoom: {self.zoom}" ) try: - self.camera_control.absolute_move(self.rho_c, self.tau_c, self.zoom, 50) + self.camera_control.absolute_move( + self.rho_c, self.tau_c, self.zoom, 50 + ) except Exception as e: logging.error(f"Error: {e}") else: @@ -415,13 +417,15 @@ def decode_payload( payload = msg.payload.decode() else: payload = msg - try: + try: json_payload = json.loads(payload) data_payload = json_payload[data_payload_type] except (KeyError, TypeError) as e: logging.error(f"Error: {e}") logging.error(json_payload) - logging.error(f"Data payload type: {data_payload_type} not found in payload: {data_payload}") + logging.error( + f"Data payload type: {data_payload_type} not found in payload: {data_payload}" + ) return {} return json.loads(data_payload) @@ -452,7 +456,9 @@ def _config_callback( # key data = self.decode_payload(msg, "Configuration") if "axis-ptz-controller" not in data: - logging.info(f"Configuration message data missing axis-ptz-controller: {data}") + logging.info( + f"Configuration message data missing axis-ptz-controller: {data}" + ) return logging.info(f"Processing config msg data: {data}") config = data["axis-ptz-controller"] @@ -504,7 +510,7 @@ def _config_callback( self.continue_on_exception = config.get( "continue_on_exception", self.continue_on_exception ) - self.camera_control.absolute_move( None, None, self.zoom, None ) + self.camera_control.absolute_move(None, None, self.zoom, None) # Log configuration parameters self._log_config() @@ -625,15 +631,10 @@ def _orientation_callback( ) logging.info(f"Final E_XYZ_to_uvw: {self.E_XYZ_to_uvw}") - - - - - def _reset_stop_timer(self): if hasattr(self, "_timer"): self._timer.cancel() - self._timer = threading.Timer(3, self._stop_timer_callback) + self._timer = threading.Timer(3, self._stop_timer_callback) self._timer.start() def _stop_timer_callback(self): @@ -648,7 +649,6 @@ def _stop_timer_callback(self): except Exception as e: logging.error(f"Error: {e}") - # ... existing code ... def _object_callback( @@ -771,7 +771,9 @@ def _object_callback( if self.use_camera and self.tau_o < 0: logging.info(f"Stopping image capture of object: {object_id}") self.do_capture = False - logging.info("Stopping continuous pan and tilt - Object is below the horizon") + logging.info( + "Stopping continuous pan and tilt - Object is below the horizon" + ) try: self.camera_control.stop_move() except Exception as e: @@ -782,7 +784,7 @@ def _object_callback( self.rho_c, self.tau_c, _zoom, _focus = self.camera_control.get_ptz() logging.debug(f"Camera pan and tilt: {self.rho_c}, {self.tau_c} [deg]") self._reset_stop_timer() - + # Point the camera at any new object directly if self.object_id != object_id: self.object_id = object_id diff --git a/axis-ptz-controller/axis_ptz_utilities.py b/axis-ptz-controller/axis_ptz_utilities.py index acacdfc..da3ed0b 100644 --- a/axis-ptz-controller/axis_ptz_utilities.py +++ b/axis-ptz-controller/axis_ptz_utilities.py @@ -7,7 +7,6 @@ from pathlib import Path from typing import cast, Generator, Tuple, Union -import coloredlogs import numpy as np import numpy.typing as npt import quaternion diff --git a/axis-ptz-controller/camera_configuration.py b/axis-ptz-controller/camera_configuration.py index 81eea3b..7b92cc8 100644 --- a/axis-ptz-controller/camera_configuration.py +++ b/axis-ptz-controller/camera_configuration.py @@ -1,9 +1,7 @@ # mypy: ignore-errors import datetime import logging -import os -import coloredlogs import requests from requests.auth import HTTPDigestAuth from sensecam_control import vapix_config diff --git a/axis-ptz-controller/camera_control.py b/axis-ptz-controller/camera_control.py index 4c0bc79..4821bca 100644 --- a/axis-ptz-controller/camera_control.py +++ b/axis-ptz-controller/camera_control.py @@ -1,9 +1,6 @@ import logging import os -# Disable all logs for this module -import logging - logging.getLogger("vapix_control").setLevel(logging.CRITICAL) from sensecam_control import vapix_control from typing import Tuple, Union @@ -82,7 +79,7 @@ def set_focus(self, focus: Union[int, None] = None) -> str: Returns the response from the device to the command sent. """ - try: + try: return self._camera_command({"focus": focus}) except Exception as e: logging.error(f"Error: {e}") diff --git a/axis-ptz-controller/test_integration.py b/axis-ptz-controller/test_integration.py index e22f865..3584289 100644 --- a/axis-ptz-controller/test_integration.py +++ b/axis-ptz-controller/test_integration.py @@ -6,7 +6,6 @@ import time from typing import Any, Dict -import coloredlogs from matplotlib import pyplot as plt import pandas as pd diff --git a/axis-ptz-controller/test_modules.py b/axis-ptz-controller/test_modules.py index 13ae717..19a7d69 100644 --- a/axis-ptz-controller/test_modules.py +++ b/axis-ptz-controller/test_modules.py @@ -55,7 +55,7 @@ def R_pole() -> float: @pytest.fixture -def controller() -> AxisPtzController: +def controller() -> axis_ptz_controller.AxisPtzController: """Construct a controller.""" controller = axis_ptz_controller.AxisPtzController( hostname=os.environ.get("HOSTNAME", ""), @@ -265,7 +265,9 @@ def test_compute_pan_rate_index( assert controller._compute_pan_rate_index(0.0) == 0 assert controller._compute_pan_rate_index(PAN_RATE_MAX * 2.0) == +100 - def test_compute_tilt_rate_index(self, controller: AxisPtzController) -> None: + def test_compute_tilt_rate_index( + self, controller: axis_ptz_controller.AxisPtzController + ) -> None: assert controller._compute_tilt_rate_index(-TILT_RATE_MAX * 2.0) == -100 assert controller._compute_tilt_rate_index(0.0) == 0 assert controller._compute_tilt_rate_index(TILT_RATE_MAX * 2.0) == +100 From 6091adc6c3eb9ea9adfaed4e746e31644bfa4188 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Thu, 7 Mar 2024 09:38:30 -0500 Subject: [PATCH 31/56] Add .bashrc for testing --- .bashrc | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .bashrc diff --git a/.bashrc b/.bashrc new file mode 100644 index 0000000..b078364 --- /dev/null +++ b/.bashrc @@ -0,0 +1,39 @@ +export PROJECT_NAME=skyscan +export HOSTNAME=dev_laptop + +export MQTT_IP=100.98.5.102 + +export CONFIG_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/Config/edgetech-c2/JSON +export ORIENTATION_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/Orientation/edgetech-auto-orienter/JSON +export OBJECT_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/Object/shipscan-c2/JSON +export IMAGE_FILENAME_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/Image_Filename/edgetech-axis-ptz-controller/JSON +export IMAGE_CAPTURE_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/Image_Capture/edgetech-axis-ptz-controller/JSON +export LOGGER_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/Logger/edgetech-axis-ptz-controller/JSON + +export CAMERA_IP=100.104.43.27 +export CAMERA_USER=root +export CAMERA_PASSWORD=iqtpass1 + +export TRIPOD_LATITUDE=38.92218155926105 +export TRIPOD_LONGITUDE=-77.22231710406469 +export TRIPOD_ALTITUDE=150.0 +export HEARTBEAT_INTERVAL=10 +export LOOP_INTERVAL=0.1 +export CAPTURE_INTERVAL=7 +export CAPTURE_DIR=../../edgetech-yolo-detect/yolo-detect/data/to_sort +export LEAD_TIME=1.0 +export PAN_GAIN=6.0 +export PAN_RATE_MIN=0.1 +export PAN_RATE_MAX=150.0 +export TILT_GAIN=6.0 +export TILT_RATE_MIN=0.1 +export TILT_RATE_MAX=150.0 +export FOCUS_SLOPE=0.0006 +export FOCUS_INTERCEPT=54 +export JPEG_RESOLUTION="1920x1080" +export JPEG_COMPRESSION=5 +export USE_MQTT=True +export USE_CAMERA=True +export AUTO_FOCUS=True +export INCLUDE_AGE=True +export LOG_TO_MQTT=False From 068faee9f52ea521af3535d11b7ace80ddf6642b Mon Sep 17 00:00:00 2001 From: spearw Date: Thu, 7 Mar 2024 16:28:43 -0500 Subject: [PATCH 32/56] Fix env variables for Test-Harness --- tests/axis-ptz-controller.env | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/axis-ptz-controller.env b/tests/axis-ptz-controller.env index 714718d..a6c99fd 100644 --- a/tests/axis-ptz-controller.env +++ b/tests/axis-ptz-controller.env @@ -5,7 +5,8 @@ CAMERA_PASSWORD=CAMERA_PASSWORD CONFIG_TOPIC=axis-config ORIENTATION_TOPIC=orientation OBJECT_TOPIC=object -CAPTURE_TOPIC=capture +IMAGE_CAPTURE_TOPIC=capture +IMAGE_FILENAME_TOPIC=filename LOGGER_TOPIC=logger HEARTBEAT_INTERVAL=10 # Sourced from a local .bashrc file for security From 0372b783810553570e654110ae8f9fa36c5d5656 Mon Sep 17 00:00:00 2001 From: spearw Date: Fri, 8 Mar 2024 13:47:22 -0500 Subject: [PATCH 33/56] Move test_modules to tests folder --- axis-ptz-controller/test_integration.py | 370 ------------------ axis-ptz-controller/test_messages.py | 265 ------------- .../A19A08-processed-track.csv | 0 .../A1E946-processed-track.csv | 0 .../acc31a_97_1_2358_2023-06-14-15-32-59.jpg | Bin .../unit-test-data}/config_msg_data.json | 0 .../unit-test-data}/object_msg_data.json | 0 .../orientation_msg_data_0s.json | 0 .../orientation_msg_data_90s.json | 0 tests/test.sh | 3 + .../test_modules.py | 10 +- 11 files changed, 8 insertions(+), 640 deletions(-) delete mode 100644 axis-ptz-controller/test_integration.py delete mode 100644 axis-ptz-controller/test_messages.py rename {axis-ptz-controller/data => tests/test-data/unit-test-data}/A19A08-processed-track.csv (100%) rename {axis-ptz-controller/data => tests/test-data/unit-test-data}/A1E946-processed-track.csv (100%) rename {axis-ptz-controller/data => tests/test-data/unit-test-data}/acc31a_97_1_2358_2023-06-14-15-32-59.jpg (100%) rename {axis-ptz-controller/data => tests/test-data/unit-test-data}/config_msg_data.json (100%) rename {axis-ptz-controller/data => tests/test-data/unit-test-data}/object_msg_data.json (100%) rename {axis-ptz-controller/data => tests/test-data/unit-test-data}/orientation_msg_data_0s.json (100%) rename {axis-ptz-controller/data => tests/test-data/unit-test-data}/orientation_msg_data_90s.json (100%) rename {axis-ptz-controller => tests}/test_modules.py (98%) diff --git a/axis-ptz-controller/test_integration.py b/axis-ptz-controller/test_integration.py deleted file mode 100644 index 3584289..0000000 --- a/axis-ptz-controller/test_integration.py +++ /dev/null @@ -1,370 +0,0 @@ -from argparse import ArgumentParser -from datetime import datetime -import json -import logging -import os -import time -from typing import Any, Dict - -from matplotlib import pyplot as plt -import pandas as pd - -from axis_ptz_controller import AxisPtzController - -HEARTBEAT_INTERVAL = 10 -LOOP_INTERVAL = 0.01 -CAPTURE_INTERVAL = 2 -LEAD_TIME = 0.0 -PAN_GAIN = 0.1 -PAN_RATE_MIN = 1.0 -PAN_RATE_MAX = 100.0 -TILT_GAIN = 0.4 -TILT_RATE_MIN = 1.0 -TILT_RATE_MAX = 100.0 -JPEG_RESOLUTION = "1920x1080" -JPEG_COMPRESSION = 5 - - -def make_controller(use_mqtt: bool) -> AxisPtzController: - """Construct a controller. - - Note that if use_mqtt = True then an MQTT broker must be started - manually. - - Parameters - ---------- - use_mqtt: bool - Flag to use MQTT, or not - - Returns - ------- - None - """ - controller = AxisPtzController( - hostname=os.environ.get("HOSTNAME", ""), - camera_ip=os.environ.get("CAMERA_IP", ""), - camera_user=os.environ.get("CAMERA_USER", ""), - camera_password=os.environ.get("CAMERA_PASSWORD", ""), - mqtt_ip=os.environ.get("MQTT_IP", ""), - config_topic=os.environ.get("CONFIG_TOPIC", ""), - orientation_topic=os.environ.get("ORIENTATION_TOPIC", ""), - object_topic=os.environ.get("OBJECT_TOPIC", ""), - image_filename_topic=os.environ.get("IMAGE_FILENAME_TOPIC", ""), - image_capture_topic=os.environ.get("IMAGE_CAPTURE_TOPIC", ""), - logger_topic=os.environ.get("LOGGER_TOPIC", ""), - heartbeat_interval=HEARTBEAT_INTERVAL, - loop_interval=LOOP_INTERVAL, - capture_interval=CAPTURE_INTERVAL, - lead_time=LEAD_TIME, - pan_gain=PAN_GAIN, - pan_rate_min=PAN_RATE_MIN, - pan_rate_max=PAN_RATE_MAX, - tilt_gain=TILT_GAIN, - tilt_rate_min=TILT_RATE_MIN, - tilt_rate_max=TILT_RATE_MAX, - jpeg_resolution=JPEG_RESOLUTION, - jpeg_compression=JPEG_COMPRESSION, - use_mqtt=use_mqtt, - use_camera=False, - include_age=False, - ) - return controller - - -def get_config_msg(controller: AxisPtzController) -> str: - """Populate a config message, reading actual, by private longitude - and latitude from the environment. - - Parameters - ---------- - None - - Returns - ------- - msg : str - The configuration message - """ - data: Dict[str, Any] = {} - data["axis-ptz-controller"] = {} - data["axis-ptz-controller"]["tripod_longitude"] = float( - os.environ.get("TRIPOD_LONGITUDE", "-77.0") - ) # [deg] - data["axis-ptz-controller"]["tripod_latitude"] = float( - os.environ.get("TRIPOD_LATITUDE", "38.0") - ) # [deg] - data["axis-ptz-controller"]["tripod_altitude"] = 86.46 # [m] - msg = controller.generate_payload_json( - push_timestamp=int(datetime.utcnow().timestamp()), - device_type="TBC", - id_="TBC", - deployment_id="TBC", - current_location="TBC", - status="Debug", - message_type="Event", - model_version="null", - firmware_version="v0.0.0", - data_payload_type="Configuration", - data_payload=json.dumps(data), - ) - return msg - - -def get_orientation_msg(controller: AxisPtzController) -> str: - """Populate an orientation message with all 0 deg angles. - - Parameters - ---------- - None - - Returns - ------- - msg : str - The orientation message - """ - with open("data/orientation_msg_data_0s.json", "r") as f: - data = json.load(f) - msg = controller.generate_payload_json( - push_timestamp=int(datetime.utcnow().timestamp()), - device_type="TBC", - id_="TBC", - deployment_id="TBC", - current_location="TBC", - status="Debug", - message_type="Event", - model_version="null", - firmware_version="v0.0.0", - data_payload_type="Orientation", - data_payload=json.dumps(data), - ) - return msg - - -def make_object_msg( - controller: AxisPtzController, track: pd.DataFrame, index: int -) -> str: - """Populate a object message with track data at the specified - index. - - Parameters - ---------- - track : pd.DataFrame() - The track data - index : int - The index of the track data to use - - Returns - ------- - msg : str - The object message - """ - data = track.iloc[index, :].to_dict() - msg = controller.generate_payload_json( - push_timestamp=int(datetime.utcnow().timestamp()), - device_type="TBC", - id_="TBC", - deployment_id="TBC", - current_location="TBC", - status="Debug", - message_type="Event", - model_version="null", - firmware_version="v0.0.0", - data_payload_type="Selected Object", - data_payload=json.dumps(data), - ) - return msg - - -def read_track_data(track_id: str) -> pd.DataFrame: - """Read a track file and convert to standard units of measure. - - Parameters - ---------- - track_id : str - The track identifier - - Returns - ------- - track : pd.DataFrame() - The track data read from the file - """ - track = pd.read_csv(f"data/{track_id}-processed-track.csv") - track["altitude"] *= 0.3048 # [ft] * [m/ft] = [m] - track["horizontal_velocity"] *= ( - 6076.12 / 3600 * 0.3048 - ) # [nm/h] * [ft/nm] / [s/h] * [m/ft] = [m/s] - track["vertical_velocity"] *= 0.3048 / 60 # [ft/s] * [m/ft] / [s/m] = [m/s] - return track - - -def plot_time_series(ts: pd.DataFrame) -> None: - """Plot time series produced by processing messages. - - Parameters - ---------- - ts : pd.DataFrame() - Dataframe containing time series to plot - - Returns - ------- - None - """ - - # Plot pan angle - fig, axs = plt.subplots(2, 2, figsize=[12.8, 9.6]) - axs[0, 0].plot(ts["timestamp_c"], ts["rho_c"] - ts["rho_o"], label="error") - axs[0, 0].plot(ts["timestamp_c"], ts["rho_c"], label="camera") - axs[0, 0].plot(ts["timestamp_c"], ts["rho_o"], label="object") - axs[0, 0].legend() - axs[0, 0].set_title("Camera and Object Pan Angle and Difference") - axs[0, 0].set_xlabel("Timestamp [s]") - axs[0, 0].set_ylabel("Pan Angle [deg]") - - # Plot tilt angle - axs[1, 0].plot(ts["timestamp_c"], ts["tau_c"] - ts["tau_o"], label="error") - axs[1, 0].plot(ts["timestamp_c"], ts["tau_c"], label="camera") - axs[1, 0].plot(ts["timestamp_c"], ts["tau_o"], label="object") - axs[1, 0].legend() - axs[1, 0].set_title("Camera and Object Tilt Angle and Difference") - axs[1, 0].set_xlabel("Timestamp [s]") - axs[1, 0].set_ylabel("Tilt Angle [deg]") - - # Plot pan angular rate angle - axs[0, 1].plot(ts["timestamp_c"], ts["rho_dot_c"] - ts["rho_dot_o"], label="error") - axs[0, 1].plot(ts["timestamp_c"], ts["rho_dot_c"], label="camera") - axs[0, 1].plot(ts["timestamp_c"], ts["rho_dot_o"], label="object") - axs[0, 1].legend() - axs[0, 1].set_title("Camera and Object Pan Angular Rate and Difference") - axs[0, 0].set_xlabel("Timestamp [s]") - axs[0, 1].set_ylabel("Pan Anglular Rate [deg/s]") - - # Plot tilt angular rate angle - axs[1, 1].plot(ts["timestamp_c"], ts["tau_dot_c"] - ts["tau_dot_o"], label="error") - axs[1, 1].plot(ts["timestamp_c"], ts["tau_dot_c"], label="camera") - axs[1, 1].plot(ts["timestamp_c"], ts["tau_dot_o"], label="object") - axs[1, 1].legend() - axs[1, 1].set_title("Camera and Object Tilt Angular Rate and Difference") - axs[0, 0].set_xlabel("Timestamp [s]") - axs[1, 1].set_ylabel("Tilt Anglular Rate [deg/s]") - - plt.show() - - -def main() -> None: - """Read a track file and process the corresponding messages using - MQTT, or not. - - Parameters - ---------- - None - - Returns - ------- - None - """ - # Provide for some command line arguments - parser = ArgumentParser( - description="Read a track file and process the corresponding messages" - ) - parser.add_argument( - "-t", - "--track-id", - default="A1E946", - help="The track identifier to process: A1E946 (the default) or A19A08", - ) - parser.add_argument( - "-m", - "--use-mqtt", - action="store_true", - help="Use MQTT to process messages", - ) - args = parser.parse_args() - - # Read the track data - logging.info(f"Reading track for id: {args.track_id}") - track = read_track_data(args.track_id) - - # Make the controller, subscribe to all topics, and publish, or - # process, one message to each topic - logging.info("Making the controller, and subscribing to topics") - controller = make_controller(args.use_mqtt) - controller.add_subscribe_topic(controller.config_topic, controller._config_callback) - controller.add_subscribe_topic( - controller.orientation_topic, controller._orientation_callback - ) - controller.add_subscribe_topic(controller.object_topic, controller._object_callback) - config_msg = get_config_msg(controller) - orientation_msg = get_orientation_msg(controller) - index = 0 - object_msg = make_object_msg(controller, track, index) - if controller.use_mqtt: - logging.info(f"Publishing config msg: {config_msg}") - controller.publish_to_topic(controller.config_topic, config_msg) - time.sleep(LOOP_INTERVAL) - - logging.info(f"Publishing orientation msg: {orientation_msg}") - controller.publish_to_topic(controller.orientation_topic, orientation_msg) - time.sleep(LOOP_INTERVAL) - - logging.info(f"Publishing object msg: {object_msg}") - controller.publish_to_topic(controller.object_topic, object_msg) - time.sleep(LOOP_INTERVAL) - - else: - _client = None - _userdata = None - controller._config_callback(_client, _userdata, config_msg) - controller._orientation_callback(_client, _userdata, orientation_msg) - controller._object_callback(_client, _userdata, object_msg) - - # Initialize history for plotting - history = {} - data = controller.decode_payload(object_msg, "Selected Object") - history["timestamp_c"] = [data["timestamp"]] - history["rho_o"] = [controller.rho_o] - history["tau_o"] = [controller.tau_o] - history["rho_dot_o"] = [controller.rho_dot_o] - history["tau_dot_o"] = [controller.tau_dot_o] - history["rho_c"] = [controller.rho_c] - history["tau_c"] = [controller.tau_c] - history["rho_dot_c"] = [controller.rho_dot_c] - history["tau_dot_c"] = [controller.tau_dot_c] - - # Loop in camera time - dt_c = controller.loop_interval - timestamp_c = history["timestamp_c"][0] - while index < track.shape[0] - 1: - timestamp_c += dt_c - - # Process each object message when received - if timestamp_c >= track["timestamp"][index + 1]: - index = track["timestamp"][timestamp_c >= track["timestamp"]].index[-1] - object_msg = make_object_msg(controller, track, index) - if controller.use_mqtt: - logging.info(f"Publishing object msg: {object_msg}") - controller.publish_to_topic(controller.object_topic, object_msg) - time.sleep(LOOP_INTERVAL) - - else: - controller._object_callback(_client, _userdata, object_msg) - - # Always update pointing - controller._update_pointing() - - # Append to history for plotting - history["timestamp_c"].append(timestamp_c) - history["rho_o"].append(controller.rho_o) - history["tau_o"].append(controller.tau_o) - history["rho_dot_o"].append(controller.rho_dot_o) - history["tau_dot_o"].append(controller.tau_dot_o) - history["rho_c"].append(controller.rho_c) - history["tau_c"].append(controller.tau_c) - history["rho_dot_c"].append(controller.rho_dot_c) - history["tau_dot_c"].append(controller.tau_dot_c) - - # Convert history dictionary to a dataframe, and plot - ts = pd.DataFrame.from_dict(history) - plot_time_series(ts) - - -if __name__ == "__main__": - main() diff --git a/axis-ptz-controller/test_messages.py b/axis-ptz-controller/test_messages.py deleted file mode 100644 index 8a4eff6..0000000 --- a/axis-ptz-controller/test_messages.py +++ /dev/null @@ -1,265 +0,0 @@ -from argparse import ArgumentParser -from datetime import datetime -import json -import logging -import os -import time -from typing import Any, Dict, Union - -import paho.mqtt.client as mqtt -import pandas as pd - -from base_mqtt_pub_sub import BaseMQTTPubSub -from test_integration import ( - get_config_msg, - get_orientation_msg, - make_controller, - make_object_msg, - plot_time_series, - read_track_data, -) - -LOOP_INTERVAL = 0.1 - - -class MessageHandler(BaseMQTTPubSub): - """Subscribe to all required topics, and open files for logging.""" - - def __init__( - self, - config_topic: str, - orientation_topic: str, - object_topic: str, - logger_topic: str, - **kwargs: Any, - ): - """Initialize a MessageHandler by subscribing to all required - topics, connecting to the MQTT broker, and opening logging - files. - - Parameters - ---------- - config_topic: str - MQTT topic for publishing or subscribing to configuration - messages - orientation_topic: str - MQTT topic for publishing or subscribing to orientation - messages - object_topic: str - MQTT topic for publishing or subscribing to object - messages - logger_topic: str - MQTT topic for publishing or subscribing to logger - messages - - Returns - ------- - MessageHandler - """ - # Parent class handles kwargs, including MQTT IP - super().__init__(**kwargs) - self.config_topic = config_topic - self.orientation_topic = orientation_topic - self.object_topic = object_topic - self.logger_topic = logger_topic - - # Connect MQTT client - logging.info("Connecting MQTT client") - self.connect_client() - time.sleep(5) - self.publish_registration("Message Handler Module Registration") - - # Open files for logging - self.camera_pointing_filename = "camera-pointing.csv" - self.camera_pointing_file = open(self.camera_pointing_filename, "w") - self.camera_pointing_keys = [ - "timestamp_c", - "rho_o", - "tau_o", - "rho_dot_o", - "tau_dot_o", - "rho_c", - "tau_c", - "rho_dot_c", - "tau_dot_c", - ] - self.camera_pointing_file.write(",".join(self.camera_pointing_keys) + "\n") - - def decode_payload( - self, msg: Union[mqtt.MQTTMessage, str], data_payload_type: str - ) -> Dict[Any, Any]: - """ - Decode the payload carried by a message. - - Parameters - ---------- - payload: mqtt.MQTTMessage - The MQTT message - data_payload_type: str - The data payload type - - Returns - ------- - data : Dict[Any, Any] - The data payload of the message payload - """ - if type(msg) == mqtt.MQTTMessage: - payload = msg.payload.decode() - else: - payload = msg - data_payload = json.loads(payload)[data_payload_type] - return json.loads(data_payload) - - def _logger_callback( - self, _client: mqtt.Client, _userdata: Dict[Any, Any], msg: mqtt.MQTTMessage - ) -> None: - """ - Process logging message based on keys. - - Parameters - ---------- - _client: mqtt.Client - MQTT client - _userdata: dict - Any required user data - msg: Any - A JSON string with {timestamp: ____, data: ____,} - - Returns - ------- - None - """ - data = self.decode_payload(msg, "Logger") - if "camera-pointing" in data: - logging.info(data["camera-pointing"]) - self.camera_pointing_file.write( - ",".join( - [str(data["camera-pointing"][k]) for k in self.camera_pointing_keys] - ) - + "\n" - ) - self.camera_pointing_file.flush() - elif "info" in data: - logging.info(data["info"]["message"]) - - -def make_handler() -> MessageHandler: - """Construct a MessageHandler. - - Note that an MQTT broker must be started manually. - - Parameters - ---------- - None - - Returns - ------- - handler: MessageHandler - The message handler - """ - handler = MessageHandler( - mqtt_ip=os.environ.get("MQTT_IP", ""), - config_topic=os.environ.get("CONFIG_TOPIC", ""), - orientation_topic=os.environ.get("ORIENTATION_TOPIC", ""), - object_topic=os.environ.get("OBJECT_TOPIC", ""), - logger_topic=os.environ.get("LOGGER_TOPIC", ""), - ) - return handler - - -def main() -> None: - """Read a track file and publish the corresponding messages using - MQTT. Subscribe to the logger topic and process log messages to - collect camera pointing time series for plotting. - - Parameters - ---------- - None - - Returns - ------- - None - """ - - # Provide for some command line arguments - parser = ArgumentParser( - description="Read a track file and process the corresponding messages" - ) - parser.add_argument( - "-t", - "--track-id", - default="A1E946", - help="The track identifier to process: A1E946 (the default) or A19A08", - ) - args = parser.parse_args() - - # Read the track data - logging.info(f"Reading track for id: {args.track_id}") - track = read_track_data(args.track_id) - - # Make the handler, and subscribe to the logger topic - logging.info("Making the handler, and subscribing to topics") - handler = make_handler() - handler.add_subscribe_topic(handler.logger_topic, handler._logger_callback) - data = { - "info": { - "message": "Subscribed to the logger", - } - } - logger_msg = handler.generate_payload_json( - push_timestamp=int(datetime.utcnow().timestamp()), - device_type="TBC", - id_="TBC", - deployment_id="TBC", - current_location="TBC", - status="Debug", - message_type="Event", - model_version="null", - firmware_version="v0.0.0", - data_payload_type="Logger", - data_payload=json.dumps(data), - ) - handler.publish_to_topic(handler.logger_topic, logger_msg) - - # Publish the configuration and orientation message, and the first - # object message - controller = make_controller(True) - config_msg = get_config_msg(controller) - orientation_msg = get_orientation_msg(controller) - index = 0 - object_msg = make_object_msg(controller, track, index) - logging.info(f"Publishing config msg: {config_msg}") - handler.publish_to_topic(handler.config_topic, config_msg) - time.sleep(LOOP_INTERVAL) - - logging.info(f"Publishing orientation msg: {orientation_msg}") - handler.publish_to_topic(handler.orientation_topic, orientation_msg) - time.sleep(LOOP_INTERVAL) - - logging.info(f"Publishing object msg: {object_msg}") - handler.publish_to_topic(handler.object_topic, object_msg) - time.sleep(LOOP_INTERVAL) - - # Loop in camera time - dt_c = LOOP_INTERVAL - timestamp_c = track["timestamp"][index] - while index < track.shape[0] - 1: - time.sleep(LOOP_INTERVAL) - timestamp_c += dt_c - - # Process each object message when received - if timestamp_c >= track["timestamp"][index + 1]: - index = track["timestamp"][timestamp_c >= track["timestamp"]].index[-1] - object_msg = make_object_msg(controller, track, index) - logging.info(f"Publishing object msg: {object_msg}") - handler.publish_to_topic(handler.object_topic, object_msg) - - # Read camera pointing file as a dataframe, and plot - time.sleep(5) - handler.camera_pointing_file.close() - ts = pd.read_csv(handler.camera_pointing_filename) - plot_time_series(ts) - - -if __name__ == "__main__": - main() diff --git a/axis-ptz-controller/data/A19A08-processed-track.csv b/tests/test-data/unit-test-data/A19A08-processed-track.csv similarity index 100% rename from axis-ptz-controller/data/A19A08-processed-track.csv rename to tests/test-data/unit-test-data/A19A08-processed-track.csv diff --git a/axis-ptz-controller/data/A1E946-processed-track.csv b/tests/test-data/unit-test-data/A1E946-processed-track.csv similarity index 100% rename from axis-ptz-controller/data/A1E946-processed-track.csv rename to tests/test-data/unit-test-data/A1E946-processed-track.csv diff --git a/axis-ptz-controller/data/acc31a_97_1_2358_2023-06-14-15-32-59.jpg b/tests/test-data/unit-test-data/acc31a_97_1_2358_2023-06-14-15-32-59.jpg similarity index 100% rename from axis-ptz-controller/data/acc31a_97_1_2358_2023-06-14-15-32-59.jpg rename to tests/test-data/unit-test-data/acc31a_97_1_2358_2023-06-14-15-32-59.jpg diff --git a/axis-ptz-controller/data/config_msg_data.json b/tests/test-data/unit-test-data/config_msg_data.json similarity index 100% rename from axis-ptz-controller/data/config_msg_data.json rename to tests/test-data/unit-test-data/config_msg_data.json diff --git a/axis-ptz-controller/data/object_msg_data.json b/tests/test-data/unit-test-data/object_msg_data.json similarity index 100% rename from axis-ptz-controller/data/object_msg_data.json rename to tests/test-data/unit-test-data/object_msg_data.json diff --git a/axis-ptz-controller/data/orientation_msg_data_0s.json b/tests/test-data/unit-test-data/orientation_msg_data_0s.json similarity index 100% rename from axis-ptz-controller/data/orientation_msg_data_0s.json rename to tests/test-data/unit-test-data/orientation_msg_data_0s.json diff --git a/axis-ptz-controller/data/orientation_msg_data_90s.json b/tests/test-data/unit-test-data/orientation_msg_data_90s.json similarity index 100% rename from axis-ptz-controller/data/orientation_msg_data_90s.json rename to tests/test-data/unit-test-data/orientation_msg_data_90s.json diff --git a/tests/test.sh b/tests/test.sh index d1bc512..2d5a679 100644 --- a/tests/test.sh +++ b/tests/test.sh @@ -2,6 +2,9 @@ PURPLE='\033[1;35m' NC='\033[0m' # No Color +echo -e "${PURPLE}Run unit tests${NC}" +pytest test_modules.py + echo -e "${PURPLE}Set up integration tests${NC}" source .bashrc docker-compose build axis-ptz-controller diff --git a/axis-ptz-controller/test_modules.py b/tests/test_modules.py similarity index 98% rename from axis-ptz-controller/test_modules.py rename to tests/test_modules.py index 19a7d69..4532794 100644 --- a/axis-ptz-controller/test_modules.py +++ b/tests/test_modules.py @@ -91,7 +91,7 @@ def controller() -> axis_ptz_controller.AxisPtzController: @pytest.fixture def config_msg(controller: axis_ptz_controller.AxisPtzController) -> str: """Populate a config message.""" - with open("data/config_msg_data.json", "r") as f: + with open("test-data/unit-test-data/config_msg_data.json", "r") as f: data = json.load(f) msg = controller.generate_payload_json( push_timestamp=int(datetime.utcnow().timestamp()), @@ -112,7 +112,7 @@ def config_msg(controller: axis_ptz_controller.AxisPtzController) -> str: @pytest.fixture def orientation_msg_0s(controller: axis_ptz_controller.AxisPtzController) -> str: """Populate an orientation message with all 0 deg angles.""" - with open("data/orientation_msg_data_0s.json", "r") as f: + with open("test-data/unit-test-data/orientation_msg_data_0s.json", "r") as f: data = json.load(f) msg = controller.generate_payload_json( push_timestamp=int(datetime.utcnow().timestamp()), @@ -133,7 +133,7 @@ def orientation_msg_0s(controller: axis_ptz_controller.AxisPtzController) -> str @pytest.fixture def orientation_msg_90s(controller: axis_ptz_controller.AxisPtzController) -> str: """Populate an orientation message with all 90 deg angles.""" - with open("data/orientation_msg_data_90s.json", "r") as f: + with open("test-data/unit-test-data/orientation_msg_data_90s.json", "r") as f: data = json.load(f) msg = controller.generate_payload_json( push_timestamp=int(datetime.utcnow().timestamp()), @@ -166,7 +166,7 @@ def object_msg(controller: axis_ptz_controller.AxisPtzController) -> str: E_XYZ_to_ENz, _, _, _ = axis_ptz_utilities.compute_E_XYZ_to_ENz(LAMBDA_O, VARPHI_O) v_ENz_A_o = np.matmul(E_XYZ_to_ENz, v_ENz_T_o) """ - with open("data/object_msg_data.json", "r") as f: + with open("test-data/unit-test-data/object_msg_data.json", "r") as f: data = json.load(f) msg = controller.generate_payload_json( push_timestamp=int(datetime.utcnow().timestamp()), @@ -566,7 +566,7 @@ def test_great_circle_distance( # TODO: Remove if passing data through filesystem is acceptable # Base64 encode an image from a file def test_encode_image(self) -> None: - image_filepath = Path("data/acc31a_97_1_2358_2023-06-14-15-32-59.jpg") + image_filepath = Path("test-data/unit-test-data/acc31a_97_1_2358_2023-06-14-15-32-59.jpg") with open(image_filepath, "rb") as image_file: image = image_file.read() From 7a85db0d5c812298a89df94374d58a1d1662da38 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Fri, 8 Mar 2024 14:57:18 -0500 Subject: [PATCH 34/56] Upgrade poetry --- axis-ptz-controller/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axis-ptz-controller/Dockerfile b/axis-ptz-controller/Dockerfile index 8c2244d..4b72b2e 100644 --- a/axis-ptz-controller/Dockerfile +++ b/axis-ptz-controller/Dockerfile @@ -1,5 +1,5 @@ FROM iqtlabs/edgetech-core:latest -RUN pip3 install poetry==1.5.1 +RUN pip3 install poetry==1.8.2 COPY ./pyproject.toml . COPY ./poetry.lock . From 5715be096e2f29fccb073d870d7f60478f73712b Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Fri, 8 Mar 2024 15:17:43 -0500 Subject: [PATCH 35/56] Cleanup --- axis-ptz-controller/axis_ptz_controller.py | 1 + axis-ptz-controller/axis_ptz_utilities.py | 4 +--- tests/docker-compose.yaml | 3 +-- tests/test_integration.py | 11 ++++++----- tests/test_modules.py | 4 +++- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 40a4376..a89b35c 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -3,6 +3,7 @@ AxisPtzController, and executes its main() method when run as a module. """ + import ast from datetime import datetime import json diff --git a/axis-ptz-controller/axis_ptz_utilities.py b/axis-ptz-controller/axis_ptz_utilities.py index da3ed0b..856ef4d 100644 --- a/axis-ptz-controller/axis_ptz_utilities.py +++ b/axis-ptz-controller/axis_ptz_utilities.py @@ -94,9 +94,7 @@ def compute_e_z_XYZ(d_lambda: float, d_varphi: float) -> npt.NDArray[np.float64] return e_z_XYZ -def compute_E_XYZ_to_ENz( - d_lambda: float, d_varphi: float -) -> Tuple[ +def compute_E_XYZ_to_ENz(d_lambda: float, d_varphi: float) -> Tuple[ npt.NDArray[np.float64], npt.NDArray[np.float64], npt.NDArray[np.float64], diff --git a/tests/docker-compose.yaml b/tests/docker-compose.yaml index 7984161..b20f9fa 100644 --- a/tests/docker-compose.yaml +++ b/tests/docker-compose.yaml @@ -3,7 +3,6 @@ # - core/base_mqtt_pub_sub.py # - mqtt/* # Clone iqt/labs/edgetech-test-harness and build test-harness image - version: "3" services: test-harness: @@ -37,4 +36,4 @@ services: - TRIPOD_LONGITUDE=${TRIPOD_LONGITUDE} - TRIPOD_ALTITUDE=${TRIPOD_ALTITUDE} volumes: - - ./test-data/tosort:/data/tosort \ No newline at end of file + - ./test-data/tosort:/data/tosort diff --git a/tests/test_integration.py b/tests/test_integration.py index 9fdc7d9..d01c8bc 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1,24 +1,25 @@ import json -import pandas as pd + from matplotlib import pyplot as plt +import pandas as pd def test_integration() -> None: """ - Tests integration of the message handler with the MQTT client. Designed to be run after docker-compose.test.yaml. - Recommended to run from test.sh. + Plots output for manual inspection. Designed to be run after + docker-compose.yaml. Recommended to run from test.sh. """ - + # Load output with open("./test-data/test-output.json") as output_file: output = json.load(output_file) + # Construct and plot time series ts = pd.DataFrame.from_dict( [ json.loads(d["message_content"]["Logger"])["camera-pointing"] for d in output[:-1] ] ) - plot_time_series(ts) diff --git a/tests/test_modules.py b/tests/test_modules.py index 4532794..0b52fc8 100644 --- a/tests/test_modules.py +++ b/tests/test_modules.py @@ -566,7 +566,9 @@ def test_great_circle_distance( # TODO: Remove if passing data through filesystem is acceptable # Base64 encode an image from a file def test_encode_image(self) -> None: - image_filepath = Path("test-data/unit-test-data/acc31a_97_1_2358_2023-06-14-15-32-59.jpg") + image_filepath = Path( + "test-data/unit-test-data/acc31a_97_1_2358_2023-06-14-15-32-59.jpg" + ) with open(image_filepath, "rb") as image_file: image = image_file.read() From e88d5ecf54475600365f585362dc372a47dbfa7c Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 11 Mar 2024 15:03:44 -0400 Subject: [PATCH 36/56] Updated named arguments --- .bashrc | 11 ++-- axis-ptz-controller/axis_ptz_controller.py | 63 ++++++++++++---------- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/.bashrc b/.bashrc index b078364..0e0ec65 100644 --- a/.bashrc +++ b/.bashrc @@ -22,12 +22,13 @@ export LOOP_INTERVAL=0.1 export CAPTURE_INTERVAL=7 export CAPTURE_DIR=../../edgetech-yolo-detect/yolo-detect/data/to_sort export LEAD_TIME=1.0 -export PAN_GAIN=6.0 +export PAN_GAIN=25.0 export PAN_RATE_MIN=0.1 -export PAN_RATE_MAX=150.0 -export TILT_GAIN=6.0 +export PAN_RATE_MAX=350.0 +export TILT_GAIN=25.0 export TILT_RATE_MIN=0.1 -export TILT_RATE_MAX=150.0 +export TILT_RATE_MAX=350.0 +export ZOOM=3000 export FOCUS_SLOPE=0.0006 export FOCUS_INTERCEPT=54 export JPEG_RESOLUTION="1920x1080" @@ -37,3 +38,5 @@ export USE_CAMERA=True export AUTO_FOCUS=True export INCLUDE_AGE=True export LOG_TO_MQTT=False +export LOG_LEVEL=INFO +export CONTINUE_ON_EXCEPTION=False diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index a89b35c..1a647da 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -55,19 +55,20 @@ def __init__( capture_interval: int = 2, capture_dir: str = ".", lead_time: float = 0.5, - yaw: float = 0.0, - pitch: float = 0.0, - roll: float = 0.0, - zoom: int = 2000, + tripod_yaw: float = 0.0, + tripod_pitch: float = 0.0, + tripod_roll: float = 0.0, pan_gain: float = 0.2, pan_rate_min: float = 1.8, pan_rate_max: float = 150.0, tilt_gain: float = 0.2, tilt_rate_min: float = 1.8, tilt_rate_max: float = 150.0, + zoom: int = 2000, + focus: int = 60, focus_slope: float = 0.0006, focus_intercept: float = 54.0, - focus_min: int = 5555, + focus_min: int = 7499, focus_max: int = 9999, jpeg_resolution: str = "1920x1080", jpeg_compression: int = 5, @@ -121,14 +122,12 @@ def __init__( lead_time: float Lead time used when computing camera pointing to the object [s] - yaw: float - Yaw correction to the camera's positioning [degrees] - pitch: float - Pitch correction to the camera's positioning [degrees] - roll: float - Roll correction to the camera's positioning [degrees] - zoom: int - Camera zoom level [0-9999] + tripod_yaw: float + Yaw angle of camera tripod from level North [degrees] + tripod_pitch: float + Pitch angle of camera tripd from level North [degrees] + tripod_roll: float + Roll angle of camera tripd from level North [degrees] pan_gain: float Proportional control gain for pan error [1/s] pan_rate_min: float @@ -141,6 +140,10 @@ def __init__( Camera tilt rate minimum [deg/s] tilt_rate_max: float Camera tilt rate maximum [deg/s] + zoom: int + Camera zoom level [0-9999] + focus: int + Camera focus [0-100%] focus_slope: float Focus slope from measurement [%/m] focus_intercept: float @@ -199,6 +202,8 @@ def __init__( self.tilt_gain = tilt_gain self.tilt_rate_min = tilt_rate_min self.tilt_rate_max = tilt_rate_max + self.zoom = zoom + self.focus = focus self.focus_slope = focus_slope self.focus_intercept = focus_intercept self.focus_min = focus_min @@ -214,9 +219,9 @@ def __init__( self.continue_on_exception = continue_on_exception # Tripod yaw, pitch, and roll angles - self.alpha = yaw # [deg] - self.beta = pitch # [deg] - self.gamma = roll # [deg] + self.alpha = tripod_yaw # [deg] + self.beta = tripod_pitch # [deg] + self.gamma = tripod_roll # [deg] # Always construct camera configuration and control since # instantiation only assigns arguments @@ -294,13 +299,10 @@ def __init__( self.rho_dot_o = 0.0 # [deg/s] self.tau_dot_o = 0.0 # [deg/s] - # Time of pointing update, camera pan and tilt angles, zoom, - # and focus + # Time of pointing update, camera pan and tilt angles self.timestamp_c = 0.0 # [s] self.rho_c = 0.0 # [deg] self.tau_c = 0.0 # [deg] - self.zoom = zoom # [0-9999] - self.focus = 60 # 0 to 100 [%] # Camera pan and tilt rates self.rho_dot_c = 0.0 # [deg/s] @@ -488,7 +490,6 @@ def _config_callback( ) # [s] self.capture_dir = config.get("capture_dir", self.capture_dir) self.lead_time = config.get("lead_time", self.lead_time) # [s] - self.zoom = config.get("zoom", self.zoom) # [0-9999] self.pan_gain = config.get("pan_gain", self.pan_gain) # [1/s] self.pan_rate_min = config.get("pan_rate_min", self.pan_rate_min) self.pan_rate_max = config.get("pan_rate_max", self.pan_rate_max) @@ -496,6 +497,8 @@ def _config_callback( self.tilt_gain = config.get("tilt_gain", self.tilt_gain) self.tilt_rate_min = config.get("tilt_rate_min", self.tilt_rate_min) self.tilt_rate_max = config.get("tilt_rate_max", self.tilt_rate_max) + self.zoom = config.get("zoom", self.zoom) # [0-9999] + self.focus = config.get("focus", self.focus) # [0-100%] self.focus_slope = config.get("focus_slope", self.focus_slope) self.focus_intercept = config.get("focus_intercept", self.focus_intercept) self.focus_min = config.get("focus_min", self.focus_min) @@ -547,10 +550,9 @@ def _log_config(self) -> None: "lambda_t": self.lambda_t, "varphi_t": self.varphi_t, "h_t": self.h_t, - "yaw": self.alpha, - "pitch": self.beta, - "roll": self.gamma, - "zoom": self.zoom, + "tripod_yaw": self.alpha, + "tripod_pitch": self.beta, + "tripod_roll": self.gamma, "heartbeat_interval": self.heartbeat_interval, "loop_interval": self.loop_interval, "capture_interval": self.capture_interval, @@ -562,6 +564,8 @@ def _log_config(self) -> None: "tilt_gain": self.tilt_gain, "tilt_rate_min": self.tilt_rate_min, "tilt_rate_max": self.tilt_rate_max, + "zoom": self.zoom, + "focus": self.focus, "focus_slope": self.focus_slope, "focus_intercept": self.focus_intercept, "focus_min": self.focus_min, @@ -1302,16 +1306,17 @@ def make_controller() -> AxisPtzController: capture_interval=int(os.environ.get("CAPTURE_INTERVAL", 2)), capture_dir=os.environ.get("CAPTURE_DIR", "."), lead_time=float(os.environ.get("LEAD_TIME", 0.5)), - yaw=float(os.environ.get("YAW", 0.0)), - pitch=float(os.environ.get("PITCH", 0.0)), - roll=float(os.environ.get("ROLL", 0.0)), - zoom=int(os.environ.get("ZOOM", 2000)), + tripod_yaw=float(os.environ.get("TRIPOD_YAW", 0.0)), + tripod_pitch=float(os.environ.get("TRIPOD_PITCH", 0.0)), + tripod_roll=float(os.environ.get("TRIPOD_ROLL", 0.0)), pan_gain=float(os.environ.get("PAN_GAIN", 0.2)), pan_rate_min=float(os.environ.get("PAN_RATE_MIN", 1.8)), pan_rate_max=float(os.environ.get("PAN_RATE_MAX", 150.0)), tilt_gain=float(os.environ.get("TILT_GAIN", 0.2)), tilt_rate_min=float(os.environ.get("TILT_RATE_MIN", 1.8)), tilt_rate_max=float(os.environ.get("TILT_RATE_MAX", 150.0)), + zoom=int(os.environ.get("ZOOM", 2000)), + focus=int(os.environ.get("FOCUS", 60)), focus_slope=float(os.environ.get("FOCUS_SLOPE", 0.0006)), focus_intercept=float(os.environ.get("FOCUS_INTERCEPT", 54)), focus_min=int(os.environ.get("FOCUS_MIN", 5555)), From e6f5b502ebbb6b3088f0925da90e486498bb53d2 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 11 Mar 2024 15:03:44 -0400 Subject: [PATCH 37/56] Update named arguments --- .bashrc | 11 ++-- axis-ptz-controller/axis_ptz_controller.py | 63 ++++++++++++---------- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/.bashrc b/.bashrc index b078364..0e0ec65 100644 --- a/.bashrc +++ b/.bashrc @@ -22,12 +22,13 @@ export LOOP_INTERVAL=0.1 export CAPTURE_INTERVAL=7 export CAPTURE_DIR=../../edgetech-yolo-detect/yolo-detect/data/to_sort export LEAD_TIME=1.0 -export PAN_GAIN=6.0 +export PAN_GAIN=25.0 export PAN_RATE_MIN=0.1 -export PAN_RATE_MAX=150.0 -export TILT_GAIN=6.0 +export PAN_RATE_MAX=350.0 +export TILT_GAIN=25.0 export TILT_RATE_MIN=0.1 -export TILT_RATE_MAX=150.0 +export TILT_RATE_MAX=350.0 +export ZOOM=3000 export FOCUS_SLOPE=0.0006 export FOCUS_INTERCEPT=54 export JPEG_RESOLUTION="1920x1080" @@ -37,3 +38,5 @@ export USE_CAMERA=True export AUTO_FOCUS=True export INCLUDE_AGE=True export LOG_TO_MQTT=False +export LOG_LEVEL=INFO +export CONTINUE_ON_EXCEPTION=False diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index a89b35c..1a647da 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -55,19 +55,20 @@ def __init__( capture_interval: int = 2, capture_dir: str = ".", lead_time: float = 0.5, - yaw: float = 0.0, - pitch: float = 0.0, - roll: float = 0.0, - zoom: int = 2000, + tripod_yaw: float = 0.0, + tripod_pitch: float = 0.0, + tripod_roll: float = 0.0, pan_gain: float = 0.2, pan_rate_min: float = 1.8, pan_rate_max: float = 150.0, tilt_gain: float = 0.2, tilt_rate_min: float = 1.8, tilt_rate_max: float = 150.0, + zoom: int = 2000, + focus: int = 60, focus_slope: float = 0.0006, focus_intercept: float = 54.0, - focus_min: int = 5555, + focus_min: int = 7499, focus_max: int = 9999, jpeg_resolution: str = "1920x1080", jpeg_compression: int = 5, @@ -121,14 +122,12 @@ def __init__( lead_time: float Lead time used when computing camera pointing to the object [s] - yaw: float - Yaw correction to the camera's positioning [degrees] - pitch: float - Pitch correction to the camera's positioning [degrees] - roll: float - Roll correction to the camera's positioning [degrees] - zoom: int - Camera zoom level [0-9999] + tripod_yaw: float + Yaw angle of camera tripod from level North [degrees] + tripod_pitch: float + Pitch angle of camera tripd from level North [degrees] + tripod_roll: float + Roll angle of camera tripd from level North [degrees] pan_gain: float Proportional control gain for pan error [1/s] pan_rate_min: float @@ -141,6 +140,10 @@ def __init__( Camera tilt rate minimum [deg/s] tilt_rate_max: float Camera tilt rate maximum [deg/s] + zoom: int + Camera zoom level [0-9999] + focus: int + Camera focus [0-100%] focus_slope: float Focus slope from measurement [%/m] focus_intercept: float @@ -199,6 +202,8 @@ def __init__( self.tilt_gain = tilt_gain self.tilt_rate_min = tilt_rate_min self.tilt_rate_max = tilt_rate_max + self.zoom = zoom + self.focus = focus self.focus_slope = focus_slope self.focus_intercept = focus_intercept self.focus_min = focus_min @@ -214,9 +219,9 @@ def __init__( self.continue_on_exception = continue_on_exception # Tripod yaw, pitch, and roll angles - self.alpha = yaw # [deg] - self.beta = pitch # [deg] - self.gamma = roll # [deg] + self.alpha = tripod_yaw # [deg] + self.beta = tripod_pitch # [deg] + self.gamma = tripod_roll # [deg] # Always construct camera configuration and control since # instantiation only assigns arguments @@ -294,13 +299,10 @@ def __init__( self.rho_dot_o = 0.0 # [deg/s] self.tau_dot_o = 0.0 # [deg/s] - # Time of pointing update, camera pan and tilt angles, zoom, - # and focus + # Time of pointing update, camera pan and tilt angles self.timestamp_c = 0.0 # [s] self.rho_c = 0.0 # [deg] self.tau_c = 0.0 # [deg] - self.zoom = zoom # [0-9999] - self.focus = 60 # 0 to 100 [%] # Camera pan and tilt rates self.rho_dot_c = 0.0 # [deg/s] @@ -488,7 +490,6 @@ def _config_callback( ) # [s] self.capture_dir = config.get("capture_dir", self.capture_dir) self.lead_time = config.get("lead_time", self.lead_time) # [s] - self.zoom = config.get("zoom", self.zoom) # [0-9999] self.pan_gain = config.get("pan_gain", self.pan_gain) # [1/s] self.pan_rate_min = config.get("pan_rate_min", self.pan_rate_min) self.pan_rate_max = config.get("pan_rate_max", self.pan_rate_max) @@ -496,6 +497,8 @@ def _config_callback( self.tilt_gain = config.get("tilt_gain", self.tilt_gain) self.tilt_rate_min = config.get("tilt_rate_min", self.tilt_rate_min) self.tilt_rate_max = config.get("tilt_rate_max", self.tilt_rate_max) + self.zoom = config.get("zoom", self.zoom) # [0-9999] + self.focus = config.get("focus", self.focus) # [0-100%] self.focus_slope = config.get("focus_slope", self.focus_slope) self.focus_intercept = config.get("focus_intercept", self.focus_intercept) self.focus_min = config.get("focus_min", self.focus_min) @@ -547,10 +550,9 @@ def _log_config(self) -> None: "lambda_t": self.lambda_t, "varphi_t": self.varphi_t, "h_t": self.h_t, - "yaw": self.alpha, - "pitch": self.beta, - "roll": self.gamma, - "zoom": self.zoom, + "tripod_yaw": self.alpha, + "tripod_pitch": self.beta, + "tripod_roll": self.gamma, "heartbeat_interval": self.heartbeat_interval, "loop_interval": self.loop_interval, "capture_interval": self.capture_interval, @@ -562,6 +564,8 @@ def _log_config(self) -> None: "tilt_gain": self.tilt_gain, "tilt_rate_min": self.tilt_rate_min, "tilt_rate_max": self.tilt_rate_max, + "zoom": self.zoom, + "focus": self.focus, "focus_slope": self.focus_slope, "focus_intercept": self.focus_intercept, "focus_min": self.focus_min, @@ -1302,16 +1306,17 @@ def make_controller() -> AxisPtzController: capture_interval=int(os.environ.get("CAPTURE_INTERVAL", 2)), capture_dir=os.environ.get("CAPTURE_DIR", "."), lead_time=float(os.environ.get("LEAD_TIME", 0.5)), - yaw=float(os.environ.get("YAW", 0.0)), - pitch=float(os.environ.get("PITCH", 0.0)), - roll=float(os.environ.get("ROLL", 0.0)), - zoom=int(os.environ.get("ZOOM", 2000)), + tripod_yaw=float(os.environ.get("TRIPOD_YAW", 0.0)), + tripod_pitch=float(os.environ.get("TRIPOD_PITCH", 0.0)), + tripod_roll=float(os.environ.get("TRIPOD_ROLL", 0.0)), pan_gain=float(os.environ.get("PAN_GAIN", 0.2)), pan_rate_min=float(os.environ.get("PAN_RATE_MIN", 1.8)), pan_rate_max=float(os.environ.get("PAN_RATE_MAX", 150.0)), tilt_gain=float(os.environ.get("TILT_GAIN", 0.2)), tilt_rate_min=float(os.environ.get("TILT_RATE_MIN", 1.8)), tilt_rate_max=float(os.environ.get("TILT_RATE_MAX", 150.0)), + zoom=int(os.environ.get("ZOOM", 2000)), + focus=int(os.environ.get("FOCUS", 60)), focus_slope=float(os.environ.get("FOCUS_SLOPE", 0.0006)), focus_intercept=float(os.environ.get("FOCUS_INTERCEPT", 54)), focus_min=int(os.environ.get("FOCUS_MIN", 5555)), From f22a4eef78330b66e0dce9fd21fa08e762dbbab1 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 11 Mar 2024 15:53:51 -0400 Subject: [PATCH 38/56] Be slightly more careful with paths --- tests/test.sh | 2 +- tests/test_modules.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test.sh b/tests/test.sh index 2d5a679..d863e37 100644 --- a/tests/test.sh +++ b/tests/test.sh @@ -3,7 +3,7 @@ PURPLE='\033[1;35m' NC='\033[0m' # No Color echo -e "${PURPLE}Run unit tests${NC}" -pytest test_modules.py +python -m pytest test_modules.py echo -e "${PURPLE}Set up integration tests${NC}" source .bashrc diff --git a/tests/test_modules.py b/tests/test_modules.py index 0b52fc8..32b1088 100644 --- a/tests/test_modules.py +++ b/tests/test_modules.py @@ -3,12 +3,15 @@ import math import os from pathlib import Path +import sys import numpy as np import numpy.typing as npt import pytest import quaternion +sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), "axis-ptz-controller")) + import axis_ptz_controller import axis_ptz_utilities From df2a908b3d6a45949b7854f522c2e7073d6e4c83 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 11 Mar 2024 16:40:48 -0400 Subject: [PATCH 39/56] Add tripod yaw, pitch, and roll as environment variables --- .bashrc | 6 +++++- axis-ptz-controller.env | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.bashrc b/.bashrc index 0e0ec65..2327bc4 100644 --- a/.bashrc +++ b/.bashrc @@ -22,13 +22,17 @@ export LOOP_INTERVAL=0.1 export CAPTURE_INTERVAL=7 export CAPTURE_DIR=../../edgetech-yolo-detect/yolo-detect/data/to_sort export LEAD_TIME=1.0 +export TRIPOD_YAW=204.5 +export TRIPOD_PITCH=-0.6 +export TRIPOD_ROLL=-1.5 export PAN_GAIN=25.0 export PAN_RATE_MIN=0.1 export PAN_RATE_MAX=350.0 export TILT_GAIN=25.0 export TILT_RATE_MIN=0.1 export TILT_RATE_MAX=350.0 -export ZOOM=3000 +export ZOOM=7000 +export FOCUS=60 export FOCUS_SLOPE=0.0006 export FOCUS_INTERCEPT=54 export JPEG_RESOLUTION="1920x1080" diff --git a/axis-ptz-controller.env b/axis-ptz-controller.env index 8817f09..b4c7f8a 100644 --- a/axis-ptz-controller.env +++ b/axis-ptz-controller.env @@ -22,9 +22,9 @@ LOOP_INTERVAL=0.1 CAPTURE_INTERVAL=2 CAPTURE_DIR=/data/tosort LEAD_TIME=0.5 -YAW=0.0 -PITCH=0.0 -ROLL=0.0 +TRIPOD_YAW=0.0 +TRIPOD_PITCH=0.0 +TRIPOD_ROLL=0.0 PAN_GAIN=6.0 PAN_RATE_MIN=1.8 PAN_RATE_MAX=150.0 From 6cf44e7c3297c9a90fcdf4d171fcff7b1fa239f1 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 11 Mar 2024 17:08:28 -0400 Subject: [PATCH 40/56] Handle tripod yaw, pitch, and roll as alpha, beta, and gamma --- axis-ptz-controller/axis_ptz_controller.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 1a647da..c37b172 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -125,9 +125,9 @@ def __init__( tripod_yaw: float Yaw angle of camera tripod from level North [degrees] tripod_pitch: float - Pitch angle of camera tripd from level North [degrees] + Pitch angle of camera tripod from level North [degrees] tripod_roll: float - Roll angle of camera tripd from level North [degrees] + Roll angle of camera tripod from level North [degrees] pan_gain: float Proportional control gain for pan error [1/s] pan_rate_min: float @@ -196,6 +196,9 @@ def __init__( self.capture_interval = capture_interval self.capture_dir = capture_dir self.lead_time = lead_time + self.alpha = tripod_yaw + self.beta = tripod_pitch + self.gamma = tripod_roll self.pan_gain = pan_gain self.pan_rate_min = pan_rate_min self.pan_rate_max = pan_rate_max @@ -218,11 +221,6 @@ def __init__( self.log_level = log_level self.continue_on_exception = continue_on_exception - # Tripod yaw, pitch, and roll angles - self.alpha = tripod_yaw # [deg] - self.beta = tripod_pitch # [deg] - self.gamma = tripod_roll # [deg] - # Always construct camera configuration and control since # instantiation only assigns arguments logging.info("Constructing camera configuration and control") @@ -490,6 +488,9 @@ def _config_callback( ) # [s] self.capture_dir = config.get("capture_dir", self.capture_dir) self.lead_time = config.get("lead_time", self.lead_time) # [s] + self.alpha = config.get("tripod_yaw", self.alpha) # [deg] + self.beta = config.get("tripod_pitch", self.beta) # [deg] + self.gamma = config.get("tripod_roll", self.gamma) # [deg] self.pan_gain = config.get("pan_gain", self.pan_gain) # [1/s] self.pan_rate_min = config.get("pan_rate_min", self.pan_rate_min) self.pan_rate_max = config.get("pan_rate_max", self.pan_rate_max) @@ -550,14 +551,14 @@ def _log_config(self) -> None: "lambda_t": self.lambda_t, "varphi_t": self.varphi_t, "h_t": self.h_t, - "tripod_yaw": self.alpha, - "tripod_pitch": self.beta, - "tripod_roll": self.gamma, "heartbeat_interval": self.heartbeat_interval, "loop_interval": self.loop_interval, "capture_interval": self.capture_interval, "capture_dir": self.capture_dir, "lead_time": self.lead_time, + "tripod_yaw": self.alpha, + "tripod_pitch": self.beta, + "tripod_roll": self.gamma, "pan_gain": self.pan_gain, "pan_rate_min": self.pan_rate_min, "pan_rate_max": self.pan_rate_max, From b8f1ad6257274edd1298131fa97c1633d1951c4e Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Tue, 12 Mar 2024 10:44:54 -0400 Subject: [PATCH 41/56] Reset focus min --- axis-ptz-controller/axis_ptz_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index c37b172..ba6fd36 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -68,7 +68,7 @@ def __init__( focus: int = 60, focus_slope: float = 0.0006, focus_intercept: float = 54.0, - focus_min: int = 7499, + focus_min: int = 5555, focus_max: int = 9999, jpeg_resolution: str = "1920x1080", jpeg_compression: int = 5, From 5c8d71a922bb7fee05e2c8b60b941a79fa32374f Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Tue, 12 Mar 2024 14:56:53 -0400 Subject: [PATCH 42/56] Make minor documentation improvement --- axis-ptz-controller/axis_ptz_controller.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index ba6fd36..69f3594 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -3,7 +3,6 @@ AxisPtzController, and executes its main() method when run as a module. """ - import ast from datetime import datetime import json @@ -143,7 +142,7 @@ def __init__( zoom: int Camera zoom level [0-9999] focus: int - Camera focus [0-100%] + Camera focus level [0-100%] focus_slope: float Focus slope from measurement [%/m] focus_intercept: float From 0ffab14d9deb114def20c03c1ed63100d7061434 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Tue, 12 Mar 2024 14:57:15 -0400 Subject: [PATCH 43/56] Remove config setting of tripod yaw, pitch, and roll because of side effects --- axis-ptz-controller/axis_ptz_controller.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 69f3594..4ee8df5 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -487,9 +487,7 @@ def _config_callback( ) # [s] self.capture_dir = config.get("capture_dir", self.capture_dir) self.lead_time = config.get("lead_time", self.lead_time) # [s] - self.alpha = config.get("tripod_yaw", self.alpha) # [deg] - self.beta = config.get("tripod_pitch", self.beta) # [deg] - self.gamma = config.get("tripod_roll", self.gamma) # [deg] + # Cannot set tripod yaw, pitch, and roll because of side effects self.pan_gain = config.get("pan_gain", self.pan_gain) # [1/s] self.pan_rate_min = config.get("pan_rate_min", self.pan_rate_min) self.pan_rate_max = config.get("pan_rate_max", self.pan_rate_max) From f62fc0ba00e33b703c5dabc24fdbaf6fad1d3711 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Thu, 14 Mar 2024 10:19:28 -0400 Subject: [PATCH 44/56] Remove .bashrc --- .bashrc | 46 ---------------------------------------------- 1 file changed, 46 deletions(-) delete mode 100644 .bashrc diff --git a/.bashrc b/.bashrc deleted file mode 100644 index 2327bc4..0000000 --- a/.bashrc +++ /dev/null @@ -1,46 +0,0 @@ -export PROJECT_NAME=skyscan -export HOSTNAME=dev_laptop - -export MQTT_IP=100.98.5.102 - -export CONFIG_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/Config/edgetech-c2/JSON -export ORIENTATION_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/Orientation/edgetech-auto-orienter/JSON -export OBJECT_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/Object/shipscan-c2/JSON -export IMAGE_FILENAME_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/Image_Filename/edgetech-axis-ptz-controller/JSON -export IMAGE_CAPTURE_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/Image_Capture/edgetech-axis-ptz-controller/JSON -export LOGGER_TOPIC=/${PROJECT_NAME}/${HOSTNAME}/Logger/edgetech-axis-ptz-controller/JSON - -export CAMERA_IP=100.104.43.27 -export CAMERA_USER=root -export CAMERA_PASSWORD=iqtpass1 - -export TRIPOD_LATITUDE=38.92218155926105 -export TRIPOD_LONGITUDE=-77.22231710406469 -export TRIPOD_ALTITUDE=150.0 -export HEARTBEAT_INTERVAL=10 -export LOOP_INTERVAL=0.1 -export CAPTURE_INTERVAL=7 -export CAPTURE_DIR=../../edgetech-yolo-detect/yolo-detect/data/to_sort -export LEAD_TIME=1.0 -export TRIPOD_YAW=204.5 -export TRIPOD_PITCH=-0.6 -export TRIPOD_ROLL=-1.5 -export PAN_GAIN=25.0 -export PAN_RATE_MIN=0.1 -export PAN_RATE_MAX=350.0 -export TILT_GAIN=25.0 -export TILT_RATE_MIN=0.1 -export TILT_RATE_MAX=350.0 -export ZOOM=7000 -export FOCUS=60 -export FOCUS_SLOPE=0.0006 -export FOCUS_INTERCEPT=54 -export JPEG_RESOLUTION="1920x1080" -export JPEG_COMPRESSION=5 -export USE_MQTT=True -export USE_CAMERA=True -export AUTO_FOCUS=True -export INCLUDE_AGE=True -export LOG_TO_MQTT=False -export LOG_LEVEL=INFO -export CONTINUE_ON_EXCEPTION=False From d04beba30ecc2ed9fe2c6f643282c5e7da365600 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Thu, 14 Mar 2024 12:18:21 -0400 Subject: [PATCH 45/56] Blacken --- axis-ptz-controller/axis_ptz_utilities.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/axis-ptz-controller/axis_ptz_utilities.py b/axis-ptz-controller/axis_ptz_utilities.py index 856ef4d..da3ed0b 100644 --- a/axis-ptz-controller/axis_ptz_utilities.py +++ b/axis-ptz-controller/axis_ptz_utilities.py @@ -94,7 +94,9 @@ def compute_e_z_XYZ(d_lambda: float, d_varphi: float) -> npt.NDArray[np.float64] return e_z_XYZ -def compute_E_XYZ_to_ENz(d_lambda: float, d_varphi: float) -> Tuple[ +def compute_E_XYZ_to_ENz( + d_lambda: float, d_varphi: float +) -> Tuple[ npt.NDArray[np.float64], npt.NDArray[np.float64], npt.NDArray[np.float64], From c16afcce20c8aa3eb68e7f5a0b19d19f3bb1eef1 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Thu, 14 Mar 2024 12:19:37 -0400 Subject: [PATCH 46/56] Lint --- axis-ptz-controller/camera_control.py | 1 - 1 file changed, 1 deletion(-) diff --git a/axis-ptz-controller/camera_control.py b/axis-ptz-controller/camera_control.py index 4821bca..f71b177 100644 --- a/axis-ptz-controller/camera_control.py +++ b/axis-ptz-controller/camera_control.py @@ -1,5 +1,4 @@ import logging -import os logging.getLogger("vapix_control").setLevel(logging.CRITICAL) from sensecam_control import vapix_control From 452c1d85d976c304266412f5ca55927f44edaf60 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Thu, 14 Mar 2024 12:31:15 -0400 Subject: [PATCH 47/56] Type --- axis-ptz-controller/axis_ptz_controller.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 4ee8df5..0c51229 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -634,13 +634,13 @@ def _orientation_callback( ) logging.info(f"Final E_XYZ_to_uvw: {self.E_XYZ_to_uvw}") - def _reset_stop_timer(self): + def _reset_stop_timer(self) -> None: if hasattr(self, "_timer"): - self._timer.cancel() + self._timer.cancel() # type: ignore self._timer = threading.Timer(3, self._stop_timer_callback) self._timer.start() - def _stop_timer_callback(self): + def _stop_timer_callback(self) -> None: # Call your function here print("Timer callback called") From b985e39ca4f677089bc25bf62c33257cfc049621 Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sun, 17 Mar 2024 22:04:14 -0400 Subject: [PATCH 48/56] Check for required Env Vars --- axis-ptz-controller/axis_ptz_controller.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 6540604..5ce704e 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -1352,9 +1352,31 @@ def main(self) -> None: else: raise +def _check_required_env_vars() -> None: + """Check that all required environment variables are set.""" + required_env_vars = [ + "MQTT_IP", + "CONFIG_TOPIC", + "ORIENTATION_TOPIC", + "OBJECT_TOPIC", + "IMAGE_FILENAME_TOPIC", + "IMAGE_CAPTURE_TOPIC", + "LOGGER_TOPIC", + "MANUAL_CONTROL_TOPIC", + "TRIPOD_LONGITUDE", + "TRIPOD_LATITUDE", + + ] + for env_var in required_env_vars: + if env_var not in os.environ: + raise ValueError(f"Environment variable {env_var} is not set") + def make_controller() -> AxisPtzController: """Instantiate an AxisPtzController.""" + + _check_required_env_vars() + return AxisPtzController( hostname=str(os.environ.get("HOSTNAME")), camera_ip=os.environ.get("CAMERA_IP", ""), From 1b3cb20431980f88f71ccc31a2561f7829de276d Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sun, 17 Mar 2024 22:54:09 -0400 Subject: [PATCH 49/56] additional logging data --- axis-ptz-controller/axis_ptz_controller.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 5ce704e..7ff1344 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -950,6 +950,10 @@ def _object_callback( "tau_c": self.tau_c, "rho_dot_c": self.rho_dot_c, "tau_dot_c": self.tau_dot_c, + "delta_rho_dot_c": self.delta_rho_dot_c, + "delta_tau_dot_c": self.delta_tau_dot_c, + "delta_rho": self._compute_angle_delta(self.rho_c, self.rho_o), + "delta_tau": self._compute_angle_delta(self.tau_c, self.tau_o), } } ), From e7e730c9e7dbdf8e8a0b065268f44837377edbc1 Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Mon, 18 Mar 2024 05:36:09 -0700 Subject: [PATCH 50/56] improved logging --- axis-ptz-controller/axis_ptz_controller.py | 1 + 1 file changed, 1 insertion(+) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 7ff1344..bac1edf 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -954,6 +954,7 @@ def _object_callback( "delta_tau_dot_c": self.delta_tau_dot_c, "delta_rho": self._compute_angle_delta(self.rho_c, self.rho_o), "delta_tau": self._compute_angle_delta(self.tau_c, self.tau_o), + "object_id": self.object_id, } } ), From 34abd219291351571a02517ff1e844ad29444a9b Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 18 Mar 2024 11:59:56 -0400 Subject: [PATCH 51/56] Use poetry package-mode False since distribution by container rather than package --- axis-ptz-controller/pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/axis-ptz-controller/pyproject.toml b/axis-ptz-controller/pyproject.toml index c8207ec..4499875 100644 --- a/axis-ptz-controller/pyproject.toml +++ b/axis-ptz-controller/pyproject.toml @@ -4,6 +4,7 @@ version = "0.1.0" description = "AXIS PTZ network camera controller" authors = ["Raymond LeClair "] license = "Apache 2.0" +package-mode = false [tool.poetry.dependencies] python = "^3.11" From 6687c8f2ea77858b3710cff21cdce36a5e817a4a Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Mon, 18 Mar 2024 12:00:26 -0400 Subject: [PATCH 52/56] Add manual control topic to tests --- tests/axis-ptz-controller.env | 1 + tests/test_modules.py | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/axis-ptz-controller.env b/tests/axis-ptz-controller.env index a6c99fd..b39181b 100644 --- a/tests/axis-ptz-controller.env +++ b/tests/axis-ptz-controller.env @@ -7,6 +7,7 @@ ORIENTATION_TOPIC=orientation OBJECT_TOPIC=object IMAGE_CAPTURE_TOPIC=capture IMAGE_FILENAME_TOPIC=filename +MANUAL_CONTROL_TOPIC=manual LOGGER_TOPIC=logger HEARTBEAT_INTERVAL=10 # Sourced from a local .bashrc file for security diff --git a/tests/test_modules.py b/tests/test_modules.py index 32b1088..31fe562 100644 --- a/tests/test_modules.py +++ b/tests/test_modules.py @@ -71,6 +71,7 @@ def controller() -> axis_ptz_controller.AxisPtzController: object_topic=os.environ.get("OBJECT_TOPIC", ""), image_filename_topic=os.environ.get("IMAGE_FILENAME_TOPIC", ""), image_capture_topic=os.environ.get("IMAGE_CAPTURE_TOPIC", ""), + manual_control_topic=os.environ.get("MANUAL_CONTROL_TOPIC", ""), logger_topic=os.environ.get("LOGGER_TOPIC", ""), heartbeat_interval=HEARTBEAT_INTERVAL, loop_interval=LOOP_INTERVAL, From 2918837d95287fc580966ad979bb26520a3283f7 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Tue, 19 Mar 2024 10:53:57 -0400 Subject: [PATCH 53/56] Re-implement auto focus --- axis-ptz-controller.env | 8 ++- axis-ptz-controller/axis_ptz_controller.py | 70 ++++++++++++---------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/axis-ptz-controller.env b/axis-ptz-controller.env index 2fd3949..b119ff8 100644 --- a/axis-ptz-controller.env +++ b/axis-ptz-controller.env @@ -32,8 +32,11 @@ PAN_RATE_MAX=150.0 TILT_GAIN=30.0 TILT_RATE_MIN=1.8 TILT_RATE_MAX=150.0 -FOCUS_SLOPE=0.0006 -FOCUS_INTERCEPT=54 +ZOOM=6000 +FOCUS=8749 +FOCUS_MIN=1749 +FOCUS_MAX=9999 +HYPERFOCAL_DISTANCE=22500.0 JPEG_RESOLUTION=1920x1080 JPEG_COMPRESSION=5 USE_MQTT=True @@ -43,4 +46,3 @@ INCLUDE_AGE=True LOG_TO_MQTT=False LOG_LEVEL=INFO CONTINUE_ON_EXCEPTION=False -LOG_LEVEL=DEBUG diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index bac1edf..c60532e 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -3,6 +3,7 @@ AxisPtzController, and executes its main() method when run as a module. """ + import ast from datetime import datetime import json @@ -64,12 +65,11 @@ def __init__( tilt_gain: float = 0.2, tilt_rate_min: float = 1.8, tilt_rate_max: float = 150.0, - zoom: int = 2000, - focus: int = 60, - focus_slope: float = 0.0006, - focus_intercept: float = 54.0, - focus_min: int = 5555, + zoom: int = 6000, + focus: int = 8749, + focus_min: int = 7499, focus_max: int = 9999, + hyperfocal_distance: float = 22500.0, jpeg_resolution: str = "1920x1080", jpeg_compression: int = 5, use_mqtt: bool = True, @@ -145,15 +145,13 @@ def __init__( zoom: int Camera zoom level [0-9999] focus: int - Camera focus level [0-100%] - focus_slope: float - Focus slope from measurement [%/m] - focus_intercept: float - Focus intercept from measurement [%] + Camera focus level [7499-9999] focus_min: int Focus minimum from settings focus_max: int Focus maximum from settings + hyperfocal_distance: float + Distance at or above which focus setting remains minimum [m] jpeg_resolution: str Image capture resolution, for example, "1920x1080" jpeg_compression: int @@ -210,10 +208,9 @@ def __init__( self.tilt_rate_max = tilt_rate_max self.zoom = zoom self.focus = focus - self.focus_slope = focus_slope - self.focus_intercept = focus_intercept self.focus_min = focus_min self.focus_max = focus_max + self.hyperfocal_distance = hyperfocal_distance self.jpeg_resolution = jpeg_resolution self.jpeg_compression = jpeg_compression self.use_mqtt = use_mqtt @@ -313,6 +310,12 @@ def __init__( self.delta_rho_dot_c = 0.0 # [deg/s] self.delta_tau_dot_c = 0.0 # [deg/s] + # Camera focus parameters. Note that the focus setting is + # minimum at and greater than the hyperfocal distance, and the + # focus setting is maximum when the distance is zero. + self.focus_slope = (self.focus_min - self.focus_max) / self.hyperfocal_distance + self.focus_intercept = self.focus_max + # Capture boolean and last capture time self.do_capture = False self.capture_time = 0.0 @@ -500,11 +503,12 @@ def _config_callback( self.tilt_rate_min = config.get("tilt_rate_min", self.tilt_rate_min) self.tilt_rate_max = config.get("tilt_rate_max", self.tilt_rate_max) self.zoom = config.get("zoom", self.zoom) # [0-9999] - self.focus = config.get("focus", self.focus) # [0-100%] - self.focus_slope = config.get("focus_slope", self.focus_slope) - self.focus_intercept = config.get("focus_intercept", self.focus_intercept) + self.focus = config.get("focus", self.focus) # [7499-9999] self.focus_min = config.get("focus_min", self.focus_min) self.focus_max = config.get("focus_max", self.focus_max) + self.hyperfocal_distance = config.get( + "hyperfocal_distance", self.hyperfocal_distance + ) self.jpeg_resolution = config.get("jpeg_resolution", self.jpeg_resolution) self.jpeg_compression = config.get("jpeg_compression", self.jpeg_compression) self.use_mqtt = config.get("use_mqtt", self.use_mqtt) @@ -568,10 +572,9 @@ def _log_config(self) -> None: "tilt_rate_max": self.tilt_rate_max, "zoom": self.zoom, "focus": self.focus, - "focus_slope": self.focus_slope, - "focus_intercept": self.focus_intercept, "focus_min": self.focus_min, "focus_max": self.focus_max, + "hyperfocal_distance": self.hyperfocal_distance, "jpeg_resolution": self.jpeg_resolution, "jpeg_compression": self.jpeg_compression, "use_mqtt": self.use_mqtt, @@ -789,7 +792,9 @@ def _object_callback( if self.use_camera: # Get camera pan and tilt self.rho_c, self.tau_c, _zoom, _focus = self.camera_control.get_ptz() - logging.debug(f"Camera pan and tilt: {self.rho_c}, {self.tau_c} [deg]") + logging.info( + f"Camera pan, tilt, zoom, and focus: {self.rho_c} [deg], {self.tau_c} [deg], {_zoom}, {_focus}" + ) self._reset_stop_timer() # Point the camera at any new object directly @@ -886,12 +891,14 @@ def _object_callback( # rates, and begin capturing images, if needed if self.use_camera: if not self.auto_focus: + # Note that focus cannot be negative, since distance3d + # is non-negative self.focus = int( - (self.focus_max - self.focus_min) - * (self.focus_slope * self.distance3d + self.focus_intercept) - / 100.0 - + self.focus_min - ) # [%] + max( + self.focus_min, + self.focus_slope * self.distance3d + self.focus_intercept, + ) + ) logging.debug(f"Commanding focus: {self.focus}") try: self.camera_control.set_focus(self.focus) @@ -952,8 +959,12 @@ def _object_callback( "tau_dot_c": self.tau_dot_c, "delta_rho_dot_c": self.delta_rho_dot_c, "delta_tau_dot_c": self.delta_tau_dot_c, - "delta_rho": self._compute_angle_delta(self.rho_c, self.rho_o), - "delta_tau": self._compute_angle_delta(self.tau_c, self.tau_o), + "delta_rho": self._compute_angle_delta( + self.rho_c, self.rho_o + ), + "delta_tau": self._compute_angle_delta( + self.tau_c, self.tau_o + ), "object_id": self.object_id, } } @@ -1412,12 +1423,11 @@ def make_controller() -> AxisPtzController: tilt_gain=float(os.environ.get("TILT_GAIN", 0.2)), tilt_rate_min=float(os.environ.get("TILT_RATE_MIN", 1.8)), tilt_rate_max=float(os.environ.get("TILT_RATE_MAX", 150.0)), - zoom=int(os.environ.get("ZOOM", 2000)), - focus=int(os.environ.get("FOCUS", 60)), - focus_slope=float(os.environ.get("FOCUS_SLOPE", 0.0006)), - focus_intercept=float(os.environ.get("FOCUS_INTERCEPT", 54)), - focus_min=int(os.environ.get("FOCUS_MIN", 5555)), + zoom=int(os.environ.get("ZOOM", 6000)), + focus=int(os.environ.get("FOCUS", 8749)), + focus_min=int(os.environ.get("FOCUS_MIN", 7499)), focus_max=int(os.environ.get("FOCUS_MAX", 9999)), + hyperfocal_distance=float(os.environ.get("HYPERFOCAL_DISTANCE", 22500.0)), jpeg_resolution=os.environ.get("JPEG_RESOLUTION", "1920x1080"), jpeg_compression=int(os.environ.get("JPEG_COMPRESSION", 5)), use_mqtt=ast.literal_eval(os.environ.get("USE_MQTT", "True")), From 325936df15a95c7fdf2a54cfa891811063fda6d7 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Tue, 19 Mar 2024 10:54:39 -0400 Subject: [PATCH 54/56] Blacken buffer --- axis-ptz-controller/axis_ptz_controller.py | 34 ++++++++++++++-------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index c60532e..45f1677 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -973,7 +973,9 @@ def _object_callback( logging.debug(f"Publishing logger msg: {logger_msg}") self.publish_to_topic(self.logger_topic, logger_msg) - def _manual_control_callback(self, _client: Union[mqtt.Client, None], + def _manual_control_callback( + self, + _client: Union[mqtt.Client, None], _userdata: Union[Dict[Any, Any], None], msg: Union[mqtt.MQTTMessage, str], ) -> None: @@ -989,8 +991,10 @@ def _manual_control_callback(self, _client: Union[mqtt.Client, None], None """ data = self.decode_payload(msg, "Manual Control") - if not set( ["azimuth", "elevation", "zoom"] ) <= set(data.keys()): - logging.info(f"Required keys missing from manual control message data: {data}") + if not set(["azimuth", "elevation", "zoom"]) <= set(data.keys()): + logging.info( + f"Required keys missing from manual control message data: {data}" + ) return azimuth = data["azimuth"] @@ -1017,16 +1021,18 @@ def _manual_control_callback(self, _client: Union[mqtt.Client, None], # Compute pan an tilt camera_pan = math.degrees(math.atan2(r_uvw_a_t[0], r_uvw_a_t[1])) # [deg] - camera_tilt = math.degrees(math.atan2(r_uvw_a_t[2], axis_ptz_utilities.norm(r_uvw_a_t[0:2]))) # [deg] + camera_tilt = math.degrees( + math.atan2(r_uvw_a_t[2], axis_ptz_utilities.norm(r_uvw_a_t[0:2])) + ) # [deg] try: - self.camera_control.absolute_move( - camera_pan, camera_tilt, self.zoom, 50 - ) + self.camera_control.absolute_move(camera_pan, camera_tilt, self.zoom, 50) except Exception as e: logging.error(f"Error: {e}") - logging.info(f"Current Camera Reading - Pan: {self.rho_c}, Tilt: {self.tau_c} \t Target - Azimuth: {azimuth} Elevation: {elevation} \t Corrected Target - Pan: {camera_pan}, Tilt: {camera_tilt} [deg]") - if elevation > 0 and azimuth > 0: + logging.info( + f"Current Camera Reading - Pan: {self.rho_c}, Tilt: {self.tau_c} \t Target - Azimuth: {azimuth} Elevation: {elevation} \t Corrected Target - Pan: {camera_pan}, Tilt: {camera_tilt} [deg]" + ) + if elevation > 0 and azimuth > 0: duration = max( math.fabs(self._compute_angle_delta(self.rho_c, azimuth)) / (self.pan_rate_max / 2), @@ -1111,7 +1117,9 @@ def _compute_angle_delta(self, theta_c: float, theta_o: float) -> float: theta_o ) if math.fabs(c) == 0: - logging.info(f"theta_c: {theta_c}, theta_o: {theta_o}, d: {d}, c: {c}, math.fabs(c): {math.fabs(c)}") + logging.info( + f"theta_c: {theta_c}, theta_o: {theta_o}, d: {d}, c: {c}, math.fabs(c): {math.fabs(c)}" + ) return 0 return math.degrees(math.acos(d)) * c / math.fabs(c) @@ -1313,7 +1321,9 @@ def main(self) -> None: self.add_subscribe_topic(self.config_topic, self._config_callback) self.add_subscribe_topic(self.orientation_topic, self._orientation_callback) self.add_subscribe_topic(self.object_topic, self._object_callback) - self.add_subscribe_topic(self.manual_control_topic, self._manual_control_callback) + self.add_subscribe_topic( + self.manual_control_topic, self._manual_control_callback + ) if self.use_camera: # Schedule image capture @@ -1368,6 +1378,7 @@ def main(self) -> None: else: raise + def _check_required_env_vars() -> None: """Check that all required environment variables are set.""" required_env_vars = [ @@ -1381,7 +1392,6 @@ def _check_required_env_vars() -> None: "MANUAL_CONTROL_TOPIC", "TRIPOD_LONGITUDE", "TRIPOD_LATITUDE", - ] for env_var in required_env_vars: if env_var not in os.environ: From c32ed9ef74ca095e9069c2d41620305aa8849fa1 Mon Sep 17 00:00:00 2001 From: Raymond LeClair Date: Tue, 19 Mar 2024 16:06:38 -0400 Subject: [PATCH 55/56] Blacken buffer --- tests/test_modules.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_modules.py b/tests/test_modules.py index 31fe562..1843c00 100644 --- a/tests/test_modules.py +++ b/tests/test_modules.py @@ -10,7 +10,9 @@ import pytest import quaternion -sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), "axis-ptz-controller")) +sys.path.append( + os.path.join(os.path.dirname(os.path.dirname(__file__)), "axis-ptz-controller") +) import axis_ptz_controller import axis_ptz_utilities From eb28030382177531f3f10a24c32817fb48ab246b Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sat, 23 Mar 2024 09:48:07 -0400 Subject: [PATCH 56/56] more logging --- axis-ptz-controller/axis_ptz_controller.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/axis-ptz-controller/axis_ptz_controller.py b/axis-ptz-controller/axis_ptz_controller.py index 45f1677..1910aa4 100644 --- a/axis-ptz-controller/axis_ptz_controller.py +++ b/axis-ptz-controller/axis_ptz_controller.py @@ -957,6 +957,8 @@ def _object_callback( "tau_c": self.tau_c, "rho_dot_c": self.rho_dot_c, "tau_dot_c": self.tau_dot_c, + "pan_rate_index": pan_rate_index, + "tilt_rate_index": tilt_rate_index, "delta_rho_dot_c": self.delta_rho_dot_c, "delta_tau_dot_c": self.delta_tau_dot_c, "delta_rho": self._compute_angle_delta(