From 922d89bf8da3bd981c013146508f0cc95b9d3eca Mon Sep 17 00:00:00 2001 From: denis defolie Date: Sun, 26 Apr 2026 18:07:29 +0200 Subject: [PATCH] tracking --- etc/6-install_django_app.sh | 1 + .../commands/reset_all_well_position.py | 36 +++++++++++++++++++ test_tube_scanner/home/settings.py | 3 +- test_tube_scanner/scanner/models.py | 26 +++++++++----- test_tube_scanner/scanner/process.py | 14 +++++--- test_tube_scanner/scanner/tasks.py | 3 +- .../scanner/templates/scanner/base.html | 8 ++--- .../templates/scanner/calibration.html | 10 +++--- test_tube_scanner/scanner/views.py | 8 ++--- 9 files changed, 79 insertions(+), 30 deletions(-) create mode 100644 test_tube_scanner/home/management/commands/reset_all_well_position.py diff --git a/etc/6-install_django_app.sh b/etc/6-install_django_app.sh index 0a4161d..4c0f2d4 100755 --- a/etc/6-install_django_app.sh +++ b/etc/6-install_django_app.sh @@ -30,6 +30,7 @@ echo "---- Tables ..." ./manage.py loaddata $ETC/db/configuration.json ./manage.py loaddata $ETC/db/well.json ./manage.py loaddata $ETC/db/multiwell.json +./manage.py reset_all_well_position --all True echo "---- start test_tube:*" echo diff --git a/test_tube_scanner/home/management/commands/reset_all_well_position.py b/test_tube_scanner/home/management/commands/reset_all_well_position.py new file mode 100644 index 0000000..6c004b9 --- /dev/null +++ b/test_tube_scanner/home/management/commands/reset_all_well_position.py @@ -0,0 +1,36 @@ +''' +Created on 19 janv. 2026 + +@author: denis +''' +from django.core.management.base import BaseCommand +#from django.conf import settings +from scanner.models import MultiWell + +class Command(BaseCommand): + help = "Recalculer les positions de tous les puits d'un multi-puits" + + def add_arguments(self, parser): + super(Command, self).add_arguments(parser) + parser.add_argument("--all", type=bool, help=f"Tous les puits de tous les multi-puits", default=False) + parser.add_argument("--position", type=str, help=f"Multiwel position: HD, HG, BD, BG", default="HD") + + def handle(self, *args, **options): # @UnusedVariable + try: + position = options.get('position') + _all = options.get('all') + + multiwells = [m for m in MultiWell.objects.filter(active=True).all() ] + for m in multiwells: + if m.position==position and not _all: + m.well_position=False + m.save() + print("Reset multi-puits position", position) + break + m.well_position=False + m.save() + print("Reset multi-puits position", position) + + except Exception as e: + print(self.help, 'erreur', e) + diff --git a/test_tube_scanner/home/settings.py b/test_tube_scanner/home/settings.py index 51df51d..d99863b 100644 --- a/test_tube_scanner/home/settings.py +++ b/test_tube_scanner/home/settings.py @@ -389,10 +389,9 @@ DATETIME_FORMAT = '%d-%m-%Y-%m %H:%M:%S' EXPORTS_LOCAL_PATH = config("EXPORTS_LOCAL_PATH") EXPORT_REMOTE_PATH = config("EXPORT_REMOTE_PATH") - EXPORT_DESTINATIONS = ["local", "remote"] -TEST_VIDEOFILE = True +TEST_VIDEOFILE = False TRACKING = True TRACKER_TUBE_AXIS = "vertical" diff --git a/test_tube_scanner/scanner/models.py b/test_tube_scanner/scanner/models.py index 7786d14..741ee72 100644 --- a/test_tube_scanner/scanner/models.py +++ b/test_tube_scanner/scanner/models.py @@ -14,12 +14,12 @@ from django.contrib.auth.models import User # Multi-well positions on the table for calibration and observation MULTIWELL_POSITION = [ - ('HG', _("Haut gauche")), - ('HD', _("Haut droit")), - ('BG', _("Bas gauche")), - ('BD', _("Bas droit")), - ('BM', _("Bas milieu")), - ('HM', _("Haut milieu")), + ('HG', _("HG-Haut gauche")), + ('HD', _("HD-Haut droit")), + ('BG', _("BG-Bas gauche")), + ('BD', _("BD-Bas droit")), + ('BM', _("BM-Bas milieu")), + ('HM', _("HM-Haut milieu")), ] FOURCC_FORMAT = [ @@ -32,6 +32,12 @@ VIDEO_TYPE = [ ('avi', _("AVI")), ] +CAPTURE_TYPE = [ + ('rpi', _("Arducam")), + ('webcam', _("Webcam")), + ('file', _("mp4")), +] + class Configuration(models.Model): name = models.CharField(_("Nom de la Configuration"), help_text=_("Nom de la configuration"), max_length=100, null=True, blank=False, default=_("Configuration par défaut")) author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Auteur", null=True, blank=True) @@ -44,10 +50,10 @@ class Configuration(models.Model): # Grbl configuration grbl_xmax = models.FloatField(_("Grbl Xmax"), help_text=_("CNC Grbl Xmax en mm"), blank=False, default=350.0) grbl_ymax = models.FloatField(_("Grbl Ymax"), help_text=_("CNC Grbl Ymax en mm"), blank=False, default=250.0) - - # camera configuration use_rpicam = models.BooleanField(_("Utiliser rpicam"), help_text=_("Par défaaut. Sinon USB webcam"), default=True) + capture_type = models.CharField(_("Capture"), help_text=_("Type de capture"), default='rpi', max_length=8, choices=CAPTURE_TYPE, null=True, blank=False) + webcam_device_index = models.PositiveSmallIntegerField(_("Index de la webcam"), help_text=_("Index de la webcam (0, 1, ...) si présente"), default=2) image_quality = models.PositiveSmallIntegerField(_("Qualité JPEG"), help_text=_("Qualité JPEG (1-100) pour les images exportées"), default=90) video_jpeg_quality = models.PositiveSmallIntegerField(_("Qualité JPEG pour les vidéos"), help_text=_("Qualité JPEG (1-100) pour les images extraites des vidéos"), default=90) @@ -60,6 +66,10 @@ class Configuration(models.Model): calibration_default_feed = models.PositiveIntegerField(_("Vitesse de calibration"), help_text=_("Vitesse de déplacement pour la calibration en mm/mn"), default=1000) calibration_default_step = models.FloatField(_("Pas de calibration"), help_text=_("Pas de déplacement pour la calibration en mm"), default=1.0) calibration_default_duration = models.FloatField(_("Duruée calibration"), help_text=_("Durée de pose entre chaque puits en s"), default=3.0) + # tracking + tracking = models.BooleanField(_("Suivi"), help_text=_("Suivi et analyse des planaires"), default=False) + + # active = models.BooleanField(_("Actif"), default=False) class Meta: diff --git a/test_tube_scanner/scanner/process.py b/test_tube_scanner/scanner/process.py index 5e99cda..dd931a5 100644 --- a/test_tube_scanner/scanner/process.py +++ b/test_tube_scanner/scanner/process.py @@ -138,9 +138,8 @@ class CameraRecordManager(): class ScannerProcess(Task): - def __init__(self, use_tracking=False): + def __init__(self): super().__init__() - self.use_tracking = use_tracking self.channel_layer = get_channel_layer() self.group = f'scanner_proc' self.stop_event = Event() @@ -162,6 +161,7 @@ class ScannerProcess(Task): def start(self, *args, **kwargs): try: self.conf = models.Configuration.objects.filter(active=True).first() + self.use_tracking = self.conf.tracking self.video_quality = self.conf.video_jpeg_quality self.image_quality = self.conf.image_quality @@ -176,7 +176,9 @@ class ScannerProcess(Task): self.grbl_ymax = self.conf.grbl_ymax self.crop = self.set_crop_radius(self.crop_radius) - if settings.TEST_VIDEOFILE: + + capture_type = self.conf.capture_type + if capture_type == 'file': video_lists = [] wells = models.Well.objects.all() for wl in wells: @@ -194,7 +196,7 @@ class ScannerProcess(Task): parent=self, video_lists=video_lists, ) - elif not self.conf.use_rpicam: + elif capture_type == 'webcam': from modules.webcam_capture import WebcamCapture self.cam = WebcamCapture( device_index=self.conf.webcam_device_index, @@ -275,6 +277,7 @@ class ScannerProcess(Task): try: (uuid, ts, frame, metrics) = self.record_queue.get() labels = dict(fps=self.video_fps, session=self.data.session, detected="1" if metrics.get("detected") else "0") + if metrics.get("detected"): labels.update({ "cx" : str(metrics["cx"]), @@ -283,7 +286,8 @@ class ScannerProcess(Task): "speed_px_s" : str(metrics["speed_px_s"]), "axial_pos" : str(metrics["axial_pos"]), "axial_speed" : str(metrics["axial_speed"]), - }) + }) + self.recordDB.write(uuid, frame, labels, ts=ts) self.record_queue.task_done() except Exception as e: diff --git a/test_tube_scanner/scanner/tasks.py b/test_tube_scanner/scanner/tasks.py index bf85812..c1e3b09 100644 --- a/test_tube_scanner/scanner/tasks.py +++ b/test_tube_scanner/scanner/tasks.py @@ -3,7 +3,6 @@ import asyncio from celery import shared_task, group, chord, chain from celery.utils.log import get_task_logger from django.utils import timezone -from django.conf import settings from .process import ScannerProcess, ReplayProcess from .export_tasks import shm_download_video, export_images_zip, export_video_mp4 @@ -19,7 +18,7 @@ class ScannerTaskManager: def start_scanner(self): if self.scanner is None: - self.scanner = ScannerProcess(use_tracking=settings.TRACKING) + self.scanner = ScannerProcess() self.scanner.start() def stop_scanner(self): diff --git a/test_tube_scanner/scanner/templates/scanner/base.html b/test_tube_scanner/scanner/templates/scanner/base.html index 24ea541..cfb855c 100644 --- a/test_tube_scanner/scanner/templates/scanner/base.html +++ b/test_tube_scanner/scanner/templates/scanner/base.html @@ -74,7 +74,7 @@ {% trans "Administration base de données" %} - + {% trans "Base de données Reductstore" %} @@ -83,13 +83,13 @@ - + {% trans "Supervisor" %} - + {% trans "Logs des planifications" %} - + {% trans "Logs des services" %} diff --git a/test_tube_scanner/scanner/templates/scanner/calibration.html b/test_tube_scanner/scanner/templates/scanner/calibration.html index 37b309f..c1587cd 100644 --- a/test_tube_scanner/scanner/templates/scanner/calibration.html +++ b/test_tube_scanner/scanner/templates/scanner/calibration.html @@ -133,17 +133,17 @@
- -
+
-
-
diff --git a/test_tube_scanner/scanner/views.py b/test_tube_scanner/scanner/views.py index 5edad37..484d938 100644 --- a/test_tube_scanner/scanner/views.py +++ b/test_tube_scanner/scanner/views.py @@ -50,7 +50,7 @@ def admin_view(request): @login_required def reductstore_view(request): - return render(request, "scanner/iframe.html", context=global_context(request, link=f'http://{settings.DOMAIN_SERVER}:8383/')) + return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.DOMAIN_SERVER}:8383/')) @login_required def adminer_view(request): @@ -62,15 +62,15 @@ def portainer_view(request): @login_required def supervisor_view(request): - return render(request, "scanner/iframe.html", context=global_context(request, link=f'http://{settings.DOMAIN_SERVER}:9001/')) + return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.DOMAIN_SERVER}:9001/')) @login_required def supervisor_worker(request): - return render(request, "scanner/iframe.html", context=global_context(request, link=f'http://{settings.DOMAIN_SERVER}:9001/logtail/test_tube:services')) + return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.DOMAIN_SERVER}:9001/logtail/test_tube:services')) @login_required def supervisor_scheduler(request): - return render(request, "scanner/iframe.html", context=global_context(request, link=f'http://{settings.DOMAIN_SERVER}:9001/logtail/test_tube:planification')) + return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.DOMAIN_SERVER}:9001/logtail/test_tube:planification')) ## Mainboard @login_required