From 7a5fe12d86156058fa22c0262e373f7c03d24dc6 Mon Sep 17 00:00:00 2001 From: petrnovota Date: Thu, 4 Jun 2026 15:58:52 +0200 Subject: [PATCH 1/2] add graceful shutdown to Hub Applications --- .../rgb-depth-connections/main.py | 19 +++++++++++++++++ apps/data-collection/backend/src/main.py | 15 +++++++++++++ apps/default-app/main.py | 21 ++++++++++++++++++- apps/dino-tracking/backend/src/main.py | 18 ++++++++++++++++ apps/focused-vision/backend/src/main.py | 15 +++++++++++++ .../backend/src/main.py | 19 +++++++++++++++++ apps/p2p-measurement/backend/src/main.py | 18 ++++++++++++++++ .../backend/src/main.py | 19 +++++++++++++++++ apps/qr-tiling/backend/src/main.py | 15 +++++++++++++ 9 files changed, 158 insertions(+), 1 deletion(-) diff --git a/apps/conference-demos/rgb-depth-connections/main.py b/apps/conference-demos/rgb-depth-connections/main.py index 471a0cafe..0cb26aeed 100755 --- a/apps/conference-demos/rgb-depth-connections/main.py +++ b/apps/conference-demos/rgb-depth-connections/main.py @@ -1,9 +1,25 @@ +import logging +import signal + import depthai as dai from utils.host_bird_eye_view import BirdsEyeView from utils.host_rgb_conference_node import CombineOutputs from utils.arguments import initialize_argparser +logger = logging.getLogger(__name__) +shutdown_requested = False + + +def _handle_shutdown_signal(_signum, _frame): + global shutdown_requested + logger.info("Application received a stop signal. Stopping the app...") + shutdown_requested = True + + +signal.signal(signal.SIGINT, _handle_shutdown_signal) +signal.signal(signal.SIGTERM, _handle_shutdown_signal) + _, args = initialize_argparser() visualizer = dai.RemoteConnection(httpPort=8082) @@ -96,6 +112,9 @@ visualizer.registerPipeline(pipeline) while pipeline.isRunning(): + if shutdown_requested: + pipeline.stop() + break key = visualizer.waitKey(1) if key == ord("q"): print("Got q key from the remote connection!") diff --git a/apps/data-collection/backend/src/main.py b/apps/data-collection/backend/src/main.py index 30a9cc2c5..d674037b2 100644 --- a/apps/data-collection/backend/src/main.py +++ b/apps/data-collection/backend/src/main.py @@ -1,4 +1,5 @@ import logging as log +import signal import depthai as dai @@ -12,6 +13,17 @@ log.basicConfig(level=log.INFO) logger = log.getLogger(__name__) +shutdown_requested = False + + +def _handle_shutdown_signal(_signum, _frame): + global shutdown_requested + logger.info("Application received a stop signal. Stopping the app...") + shutdown_requested = True + + +signal.signal(signal.SIGINT, _handle_shutdown_signal) +signal.signal(signal.SIGTERM, _handle_shutdown_signal) def main(): @@ -101,6 +113,9 @@ def main(): logger.info("Pipeline running!") while pipeline.isRunning(): + if shutdown_requested: + pipeline.stop() + break key = visualizer.waitKey(1) pipeline.processTasks() if key == ord("q"): diff --git a/apps/default-app/main.py b/apps/default-app/main.py index 222c4f66a..1887f2c5e 100644 --- a/apps/default-app/main.py +++ b/apps/default-app/main.py @@ -1,10 +1,26 @@ #!/usr/bin/env python3 +import logging +import signal +from typing import Optional + import cv2 import depthai as dai from depthai_nodes.node import ApplyDepthColormap -from typing import Optional from utils.arguments import initialize_argparser +logger = logging.getLogger(__name__) +shutdown_requested = False + + +def _handle_shutdown_signal(_signum, _frame): + global shutdown_requested + logger.info("Application received a stop signal. Stopping the app...") + shutdown_requested = True + + +signal.signal(signal.SIGINT, _handle_shutdown_signal) +signal.signal(signal.SIGTERM, _handle_shutdown_signal) + _, args = initialize_argparser() visualizer = dai.RemoteConnection(httpPort=8082) @@ -97,6 +113,9 @@ visualizer.registerPipeline(pipeline) while pipeline.isRunning(): + if shutdown_requested: + pipeline.stop() + break key = visualizer.waitKey(1) if key == ord("q"): print("Got q key from the remote connection!") diff --git a/apps/dino-tracking/backend/src/main.py b/apps/dino-tracking/backend/src/main.py index 67bac8eb0..43d993d35 100644 --- a/apps/dino-tracking/backend/src/main.py +++ b/apps/dino-tracking/backend/src/main.py @@ -1,3 +1,5 @@ +import logging +import signal from pathlib import Path import depthai as dai @@ -34,6 +36,19 @@ ) from object_selection.mask_selection_node import MaskSelection +logger = logging.getLogger(__name__) +shutdown_requested = False + + +def _handle_shutdown_signal(_signum, _frame): + global shutdown_requested + logger.info("Application received a stop signal. Stopping the app...") + shutdown_requested = True + + +signal.signal(signal.SIGINT, _handle_shutdown_signal) +signal.signal(signal.SIGTERM, _handle_shutdown_signal) + load_dotenv(override=True) @@ -168,6 +183,9 @@ def main(): visualizer.registerPipeline(pipeline) while pipeline.isRunning(): + if shutdown_requested: + pipeline.stop() + break key = visualizer.waitKey(1) if key == ord("q"): print("Received q. Exiting...") diff --git a/apps/focused-vision/backend/src/main.py b/apps/focused-vision/backend/src/main.py index e4fdc596b..8b98d820c 100644 --- a/apps/focused-vision/backend/src/main.py +++ b/apps/focused-vision/backend/src/main.py @@ -1,4 +1,5 @@ import logging +import signal import depthai as dai @@ -18,6 +19,17 @@ from pathlib import Path logger = logging.getLogger(__name__) +shutdown_requested = False + + +def _handle_shutdown_signal(_signum, _frame): + global shutdown_requested + logger.info("Application received a stop signal. Stopping the app...") + shutdown_requested = True + + +signal.signal(signal.SIGINT, _handle_shutdown_signal) +signal.signal(signal.SIGTERM, _handle_shutdown_signal) _, args = initialize_argparser() @@ -241,6 +253,9 @@ counter = 0 while pipeline.isRunning(): + if shutdown_requested: + pipeline.stop() + break pipeline.processTasks() key = visualizer.waitKey(1) counter += 1 diff --git a/apps/object-volume-measurement-3d/backend/src/main.py b/apps/object-volume-measurement-3d/backend/src/main.py index 536dbc84a..07a8fc598 100644 --- a/apps/object-volume-measurement-3d/backend/src/main.py +++ b/apps/object-volume-measurement-3d/backend/src/main.py @@ -1,3 +1,6 @@ +import logging +import signal + import depthai as dai from depthai_nodes.node import ParsingNeuralNetwork, ImgDetectionsFilter @@ -8,6 +11,19 @@ from utils.annotation_node import AnnotationNode from utils.measurement_node import MeasurementNode +logger = logging.getLogger(__name__) +shutdown_requested = False + + +def _handle_shutdown_signal(_signum, _frame): + global shutdown_requested + logger.info("Application received a stop signal. Stopping the app...") + shutdown_requested = True + + +signal.signal(signal.SIGINT, _handle_shutdown_signal) +signal.signal(signal.SIGTERM, _handle_shutdown_signal) + _, args = initialize_argparser() IP = args.ip or "localhost" @@ -229,6 +245,9 @@ def measurement_method_service(payload: dict): print("Press 'q' to stop") while pipeline.isRunning(): + if shutdown_requested: + pipeline.stop() + break pipeline.processTasks() key = visualizer.waitKey(1) if key == ord("q"): diff --git a/apps/p2p-measurement/backend/src/main.py b/apps/p2p-measurement/backend/src/main.py index b4604aa9f..649f911f7 100644 --- a/apps/p2p-measurement/backend/src/main.py +++ b/apps/p2p-measurement/backend/src/main.py @@ -1,3 +1,6 @@ +import logging +import signal + import depthai as dai from depthai_nodes.node import ApplyDepthColormap import numpy as np @@ -6,6 +9,18 @@ from utils.arguments import initialize_argparser from utils.point_tracker import PointTracker +logger = logging.getLogger(__name__) +shutdown_requested = False + + +def _handle_shutdown_signal(_signum, _frame): + global shutdown_requested + logger.info("Application received a stop signal. Stopping the app...") + shutdown_requested = True + + +signal.signal(signal.SIGINT, _handle_shutdown_signal) +signal.signal(signal.SIGTERM, _handle_shutdown_signal) _, args = initialize_argparser() @@ -138,6 +153,9 @@ def get_tracking_status_service(data: dict): visualizer.registerPipeline(pipeline) while pipeline.isRunning(): + if shutdown_requested: + pipeline.stop() + break pipeline.processTasks() key = visualizer.waitKey(1) if key == ord("q"): diff --git a/apps/people-demographics-and-sentiment-analysis/backend/src/main.py b/apps/people-demographics-and-sentiment-analysis/backend/src/main.py index e824e14cf..facc525ec 100644 --- a/apps/people-demographics-and-sentiment-analysis/backend/src/main.py +++ b/apps/people-demographics-and-sentiment-analysis/backend/src/main.py @@ -1,3 +1,6 @@ +import logging +import signal + import depthai as dai from config.arguments import initialize_argparser @@ -18,6 +21,19 @@ from visualization.monitor_node import MonitorFacesNode from visualization.annotation_node import AnnotationNode +logger = logging.getLogger(__name__) +shutdown_requested = False + + +def _handle_shutdown_signal(_signum, _frame): + global shutdown_requested + logger.info("Application received a stop signal. Stopping the app...") + shutdown_requested = True + + +signal.signal(signal.SIGINT, _handle_shutdown_signal) +signal.signal(signal.SIGTERM, _handle_shutdown_signal) + def main(): _, args = initialize_argparser() @@ -109,6 +125,9 @@ def main(): visualizer.registerPipeline(pipeline) while pipeline.isRunning(): + if shutdown_requested: + pipeline.stop() + break key = visualizer.waitKey(1) pipeline.processTasks() if key == ord("q"): diff --git a/apps/qr-tiling/backend/src/main.py b/apps/qr-tiling/backend/src/main.py index 44fc45b28..36b0e6a45 100644 --- a/apps/qr-tiling/backend/src/main.py +++ b/apps/qr-tiling/backend/src/main.py @@ -1,5 +1,6 @@ from pathlib import Path import logging +import signal import depthai as dai from depthai_nodes.node import ( @@ -29,8 +30,19 @@ logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) +shutdown_requested = False MODEL_DIR = Path(__file__).resolve().parent / "depthai_models" + +def _handle_shutdown_signal(_signum, _frame): + global shutdown_requested + logger.info("Application received a stop signal. Stopping the app...") + shutdown_requested = True + + +signal.signal(signal.SIGINT, _handle_shutdown_signal) +signal.signal(signal.SIGTERM, _handle_shutdown_signal) + visualizer = dai.RemoteConnection(httpPort=8082) device = dai.Device() @@ -171,6 +183,9 @@ logger.info("Pipeline running!") while pipeline.isRunning(): + if shutdown_requested: + pipeline.stop() + break pipeline.processTasks() key = visualizer.waitKey(1) if key == ord("q"): From 08209b67da1e6ec8d0083ca04211f0ce302ca5b9 Mon Sep 17 00:00:00 2001 From: petrnovota Date: Thu, 4 Jun 2026 16:00:31 +0200 Subject: [PATCH 2/2] bump up app versions mostly to 1.0.0 --- apps/data-collection/oakapp.toml | 2 +- apps/default-app/oakapp.toml | 2 +- apps/dino-tracking/oakapp.toml | 2 +- apps/focused-vision/oakapp.toml | 2 +- apps/object-volume-measurement-3d/oakapp.toml | 2 +- apps/p2p-measurement/oakapp.toml | 2 +- apps/people-demographics-and-sentiment-analysis/oakapp.toml | 2 +- apps/qr-tiling/oakapp.toml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/data-collection/oakapp.toml b/apps/data-collection/oakapp.toml index 41ff767fe..51b50c812 100644 --- a/apps/data-collection/oakapp.toml +++ b/apps/data-collection/oakapp.toml @@ -1,6 +1,6 @@ identifier = "com.example.apps.data-collection" entrypoint = ["bash", "-c", "/usr/bin/runsvdir -P /etc/service"] -app_version = "0.9.3" +app_version = "1.0.0" prepare_container = [ { type = "COPY", source = "./backend/requirements.txt", target = "./backend/requirements.txt" }, diff --git a/apps/default-app/oakapp.toml b/apps/default-app/oakapp.toml index 3922ba411..567cbcdcd 100644 --- a/apps/default-app/oakapp.toml +++ b/apps/default-app/oakapp.toml @@ -1,6 +1,6 @@ identifier = "com.example.apps.default-app" entrypoint = ["bash", "-c", "/usr/bin/runsvdir -P /etc/service"] -app_version = "1.0.0" +app_version = "1.0.1" assign_frontend_port = true prepare_container = [ diff --git a/apps/dino-tracking/oakapp.toml b/apps/dino-tracking/oakapp.toml index 57e1a42c7..4b97a1fe5 100644 --- a/apps/dino-tracking/oakapp.toml +++ b/apps/dino-tracking/oakapp.toml @@ -1,6 +1,6 @@ identifier = "com.example.apps.dino-tracking" entrypoint = ["bash", "-c", "/usr/bin/runsvdir -P /etc/service"] -app_version = "0.9.1" +app_version = "1.0.0" prepare_container = [ { type = "COPY", source = "./backend/requirements.txt", target = "./backend/requirements.txt" }, diff --git a/apps/focused-vision/oakapp.toml b/apps/focused-vision/oakapp.toml index 0c3833e40..655fe500f 100644 --- a/apps/focused-vision/oakapp.toml +++ b/apps/focused-vision/oakapp.toml @@ -1,6 +1,6 @@ identifier = "com.example.apps.focused-vision" entrypoint = ["bash", "-c", "/usr/bin/runsvdir -P /etc/service"] -app_version = "0.9.3" +app_version = "1.0.0" prepare_container = [ { type = "COPY", source = "./backend/requirements.txt", target = "./backend/requirements.txt" }, diff --git a/apps/object-volume-measurement-3d/oakapp.toml b/apps/object-volume-measurement-3d/oakapp.toml index b2022701e..c5a3e0a0f 100644 --- a/apps/object-volume-measurement-3d/oakapp.toml +++ b/apps/object-volume-measurement-3d/oakapp.toml @@ -1,6 +1,6 @@ identifier = "com.example.apps.object-volume-measurement-3d" entrypoint = ["bash", "-c", "/usr/bin/runsvdir -P /etc/service"] -app_version = "0.9.4" +app_version = "1.0.0" storage_dir_name = "storage" prepare_container = [ diff --git a/apps/p2p-measurement/oakapp.toml b/apps/p2p-measurement/oakapp.toml index b3f35527f..69a27c920 100644 --- a/apps/p2p-measurement/oakapp.toml +++ b/apps/p2p-measurement/oakapp.toml @@ -1,6 +1,6 @@ identifier = "com.example.apps.p2p-measurement" entrypoint = ["bash", "-c", "/usr/bin/runsvdir -P /etc/service"] -app_version = "0.9.3" +app_version = "1.0.0" storage_dir_name = "storage" prepare_container = [ diff --git a/apps/people-demographics-and-sentiment-analysis/oakapp.toml b/apps/people-demographics-and-sentiment-analysis/oakapp.toml index de9adb117..abc5ef656 100644 --- a/apps/people-demographics-and-sentiment-analysis/oakapp.toml +++ b/apps/people-demographics-and-sentiment-analysis/oakapp.toml @@ -1,6 +1,6 @@ identifier = "com.example.apps.people-demographic-sentiment-analysis" entrypoint = ["bash", "-c", "/usr/bin/runsvdir -P /etc/service"] -app_version = "0.9.2" +app_version = "1.0.0" prepare_container = [ { type = "COPY", source = "./backend/requirements.txt", target = "./backend/requirements.txt" }, diff --git a/apps/qr-tiling/oakapp.toml b/apps/qr-tiling/oakapp.toml index 5f9d6638b..7e452be87 100644 --- a/apps/qr-tiling/oakapp.toml +++ b/apps/qr-tiling/oakapp.toml @@ -1,6 +1,6 @@ identifier = "com.example.apps.qr-tiling" entrypoint = ["bash", "-c", "/usr/bin/runsvdir -P /etc/service"] -app_version = "1.0.0" +app_version = "1.0.1" prepare_container = [ { type = "RUN", command = "apt-get update" },