simulation
This commit is contained in:
@@ -239,6 +239,7 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
LOGGING_FILE = config('LOGGING_FILE')
|
||||
IS_LOGGING = config('IS_LOGGING', cast=bool)
|
||||
IS_LOGGING = False
|
||||
|
||||
LOGGING = None if not IS_LOGGING else {
|
||||
'version': 1,
|
||||
|
||||
@@ -84,7 +84,7 @@ class VideoCaptureInterface(abc.ABC):
|
||||
|
||||
self._tracker = None
|
||||
self._metrics = None
|
||||
self._paramss = None
|
||||
self._params = None
|
||||
|
||||
# Tracker générique, pour simulation
|
||||
self.on_test_well_change(**self.DEFAULT_TRACKER_CONFIG)
|
||||
@@ -94,14 +94,17 @@ class VideoCaptureInterface(abc.ABC):
|
||||
dead_zone_px = 5, # en-dessous → rien à faire
|
||||
display = display,
|
||||
)
|
||||
self.align_detection = None # résultat du test
|
||||
self.align_detection = None # résultat du test
|
||||
|
||||
|
||||
def on_test_well_change(self, **cfg):
|
||||
if self.use_tracking and cfg:
|
||||
self._tracker = PlanarianTracker(**cfg)
|
||||
logger.warning(f"New tracker conf: {cfg}")
|
||||
|
||||
try:
|
||||
if self.use_tracking and cfg:
|
||||
self._tracker = PlanarianTracker(**cfg)
|
||||
logger.info("Tracker de test créé avec conf : %s", cfg)
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating tracker with conf {cfg}: {e}")
|
||||
self._tracker = None
|
||||
|
||||
def on_well_change(self, cfg):
|
||||
"""
|
||||
@@ -112,10 +115,12 @@ class VideoCaptureInterface(abc.ABC):
|
||||
if not self.use_tracking:
|
||||
return
|
||||
|
||||
params = self.DEFAULT_TRACKER_CONFIG if not cfg else cfg.to_params_dict()
|
||||
|
||||
params = self.DEFAULT_TRACKER_CONFIG if not cfg else cfg.to_params_dict()
|
||||
self._params = ExperimentParams(params)
|
||||
self._metrics = self._params.build_metrics()
|
||||
#self._metrics = self._params.build_metrics()
|
||||
|
||||
self._metrics = [self._params.build_metrics() for _ in range(self._params.planarian_count)]
|
||||
|
||||
self._tracker = PlanarianTracker(
|
||||
tube_axis = self._params.tube_axis,
|
||||
min_area_px = self._params.min_area_px,
|
||||
@@ -265,12 +270,13 @@ class VideoCaptureInterface(abc.ABC):
|
||||
:return: Image traitée (JPEG ou PNG selon la stratégie)
|
||||
"""
|
||||
metrics = {"detected": False}
|
||||
|
||||
if self._circular_crop is not None:
|
||||
jpeg = self._circular_crop.process(jpeg_bytes)
|
||||
nparr = np.frombuffer(jpeg, np.uint8)
|
||||
frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
|
||||
if frame is None:
|
||||
return jpeg, metrics
|
||||
return jpeg, metrics
|
||||
try:
|
||||
# Mode debug
|
||||
if self._aligner.debug:
|
||||
@@ -278,7 +284,7 @@ class VideoCaptureInterface(abc.ABC):
|
||||
annotated = self.align_detection.get('frame_annotated')
|
||||
frame = annotated if annotated is not None else frame
|
||||
##
|
||||
# mode racking
|
||||
# mode tracking
|
||||
if self.use_tracking:
|
||||
ts = datetime.now(timezone.utc).timestamp()
|
||||
frame, metrics = self._tracker.process(frame, ts)
|
||||
|
||||
@@ -20,6 +20,10 @@ import cv2
|
||||
import numpy as np
|
||||
|
||||
from scipy.optimize import linear_sum_assignment
|
||||
import logging
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Nombre maximum de planaires suivis simultanément par tube
|
||||
MAX_PLANARIANS = 10
|
||||
@@ -386,7 +390,6 @@ class PlanarianTracker:
|
||||
for state in self._states:
|
||||
if state.idx not in assigned_slots:
|
||||
state.mark_lost()
|
||||
|
||||
return frame_out, results
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
@@ -135,7 +135,7 @@ class ExperimentConfig(models.Model):
|
||||
def to_params_dict(self) -> dict:
|
||||
"""Retourne un dict compatible avec ExperimentParams."""
|
||||
return {
|
||||
"experiment": self.idendifier,
|
||||
"experiment": self.identifier,
|
||||
"well": self.well.name,
|
||||
"px_per_mm": self.px_per_mm,
|
||||
"fps": self.fps,
|
||||
|
||||
@@ -24,7 +24,6 @@ class ScannerConsumer(AsyncWebsocketConsumer):
|
||||
logger.info( f"==== Disconnect from {self.this_group}")
|
||||
|
||||
async def scanner_message(self, event):
|
||||
logger.info( f"===={event.get('type')}")
|
||||
await self.send(text_data=json.dumps(event["text"]))
|
||||
|
||||
## Receive message from WebSocket
|
||||
|
||||
@@ -7,6 +7,8 @@ Created on 20 avr. 2026
|
||||
|
||||
@author: denis
|
||||
'''
|
||||
|
||||
|
||||
import logging
|
||||
import time
|
||||
from threading import Thread, Event
|
||||
@@ -18,10 +20,8 @@ from planarian.models import ExperimentConfig
|
||||
from . import models
|
||||
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WellIterator:
|
||||
"""Itérateur personnalisé pour naviguer dans les Wells"""
|
||||
|
||||
@@ -76,9 +76,12 @@ class WellIterator:
|
||||
class MultiWellManager:
|
||||
|
||||
def __init__(self, process):
|
||||
|
||||
self.process = process
|
||||
self.cnc_controller = process.grbl
|
||||
|
||||
logger.info(f"MultiWellManager initialized with CNC controller: {self.cnc_controller}")
|
||||
|
||||
self.stop_playing = Event()
|
||||
self.well_iterator = None
|
||||
self.multiwel = None
|
||||
@@ -86,6 +89,19 @@ class MultiWellManager:
|
||||
self.set_multiwell()
|
||||
self.scan_thread = None
|
||||
self.test_thread = None
|
||||
|
||||
self.tracker_config = dict(
|
||||
tube_axis = settings.TRACKER_TUBE_AXIS,
|
||||
min_area_px = self.process.conf.min_area_px,
|
||||
max_area_ratio = self.process.conf.max_area_ratio,
|
||||
max_planarians = self.process.conf.max_planarians,
|
||||
merge_kernel_size = self.process.conf.merge_kernel_size,
|
||||
min_contour_dist_px = self.process.conf.min_contour_dist_px,
|
||||
)
|
||||
|
||||
|
||||
|
||||
def set_tracker_config(self):
|
||||
self.tracker_config = dict(
|
||||
tube_axis = settings.TRACKER_TUBE_AXIS,
|
||||
min_area_px = self.process.conf.min_area_px,
|
||||
@@ -93,8 +109,8 @@ class MultiWellManager:
|
||||
max_planarians = self.process.conf.max_planarians,
|
||||
merge_kernel_size = self.process.conf.merge_kernel_size,
|
||||
min_contour_dist_px = self.process.conf.min_contour_dist_px,
|
||||
)
|
||||
|
||||
)
|
||||
logger.info(f"Tracker config: {self.tracker_config}")
|
||||
|
||||
def set_default_values(self, feed=None, step=None, duration=None):
|
||||
self._feed = feed or self.process.conf.calibration_default_feed
|
||||
@@ -136,7 +152,8 @@ class MultiWellManager:
|
||||
well = well_position.well
|
||||
multiwell = experiment.multiwell
|
||||
if self.process.use_tracking:
|
||||
# Paramètres d'une expérience PlanarianScanner
|
||||
logger.info("# Paramètres d'une expérience PlanarianScanner")
|
||||
|
||||
cfg = ExperimentConfig.objects.get(experiment_id=experiment.id, well_id=well.id)
|
||||
# reset PlanarianTracker => on_well_change
|
||||
self.process.cam.on_well_change(cfg)
|
||||
@@ -151,7 +168,7 @@ class MultiWellManager:
|
||||
while not self.stop_playing.is_set():
|
||||
if time.monotonic() - start > multiwell.duration:
|
||||
break
|
||||
self.cnc_controller.wait_for(1.0)
|
||||
self.cnc_controller.wait_for(0.1)
|
||||
|
||||
self.process.data.record = False
|
||||
self.process.data.uuid = None
|
||||
@@ -175,10 +192,12 @@ class MultiWellManager:
|
||||
|
||||
## change file
|
||||
if self.process.conf.capture_type == 'file':
|
||||
vf = settings.MEDIA_ROOT / 'simulation' / f'{wl.name}.mp4'
|
||||
vf = settings.MEDIA_ROOT / 'simulation' / f'{wl.well.name}.mp4'
|
||||
if vf.exists():
|
||||
self.process.cam._video_file = str(vf)
|
||||
self.process.cam._error_occured = True
|
||||
self.process.cam._error_occured = True
|
||||
|
||||
logger.info(f"Simulating capture with file {vf}")
|
||||
|
||||
self._grid_scanning_capture(experiment, wl, simulate=simulate)
|
||||
|
||||
@@ -188,58 +207,60 @@ class MultiWellManager:
|
||||
|
||||
|
||||
def _start_scanning(self, session, experiments, simulate=False):
|
||||
self.process.cam._aligner.debug = False
|
||||
xynext = []
|
||||
for obs in experiments:
|
||||
xynext.append((obs.multiwell.xbase, obs.multiwell.ybase))
|
||||
xynext.append((0, 0))
|
||||
|
||||
pos = 1
|
||||
self.process.data.session = session.id
|
||||
started = timezone.now()
|
||||
for obs in experiments:
|
||||
try:
|
||||
self.process.cam._aligner.debug = False
|
||||
xynext = []
|
||||
for obs in experiments:
|
||||
xynext.append((obs.multiwell.xbase, obs.multiwell.ybase))
|
||||
xynext.append((0, 0))
|
||||
|
||||
pos = 1
|
||||
self.process.data.session = session.id
|
||||
started = timezone.now()
|
||||
for obs in experiments:
|
||||
if self.stop_playing.is_set():
|
||||
break
|
||||
obs.started = timezone.now()
|
||||
obs.save()
|
||||
xnext, ynext = xynext[pos]
|
||||
pos +=1
|
||||
self._grid_scanning(obs, xnext=xnext, ynext=ynext, simulate=simulate)
|
||||
obs.finished = timezone.now()
|
||||
obs.save()
|
||||
|
||||
session.finished = timezone.now()
|
||||
if self.stop_playing.is_set():
|
||||
break
|
||||
obs.started = timezone.now()
|
||||
obs.save()
|
||||
xnext, ynext = xynext[pos]
|
||||
pos +=1
|
||||
self._grid_scanning(obs, xnext=xnext, ynext=ynext, simulate=simulate)
|
||||
obs.finished = timezone.now()
|
||||
obs.save()
|
||||
|
||||
session.finished = timezone.now()
|
||||
if self.stop_playing.is_set():
|
||||
msg = f"Session {session.name} abandonnée à {session.finished} après {session.finished - started} secondes."
|
||||
else:
|
||||
if not simulate:
|
||||
session.active = False
|
||||
if session.scanning_task:
|
||||
session.scanning_task.enabled = False
|
||||
session.save()
|
||||
|
||||
msg = f"Session {session.name} terminée à {session.finished} après {session.finished - started} secondes."
|
||||
logger.info(msg)
|
||||
self.process._send(scan_state=msg)
|
||||
self.scan_thread = None
|
||||
msg = f"Session {session.name} abandonnée à {session.finished} après {session.finished - started} secondes."
|
||||
else:
|
||||
if not simulate:
|
||||
session.active = False
|
||||
if session.scanning_task:
|
||||
session.scanning_task.enabled = False
|
||||
session.save()
|
||||
msg = f"Session {session.name} terminée à {session.finished} après {session.finished - started} secondes."
|
||||
logger.info(msg)
|
||||
self.process._send(scan_state=msg)
|
||||
finally:
|
||||
self.scan_thread = None
|
||||
|
||||
|
||||
def halt_scanning(self):
|
||||
self.process.data.record = False
|
||||
self.stop_playing.set()
|
||||
self.well_iterator.reset()
|
||||
self.process.cam._aligner.debug = False
|
||||
self.process.cam._aligner.debug = False
|
||||
self.scan_thread = None
|
||||
self.test_thread = None
|
||||
|
||||
|
||||
def scanning(self, sid, simulate=False):
|
||||
try:
|
||||
if self.scan_thread:
|
||||
return
|
||||
session = models.Session.objects.get(pk=sid)
|
||||
experiments = models.SessionExperiment.experiment_by_session(sid)
|
||||
self.scan_thread = Thread(target=self._start_scanning, args=(session, experiments, simulate, ), daemon=True).start()
|
||||
except Exception as e:
|
||||
print("MultiWellManager::scan error", e)
|
||||
def scan_process(self, sid, simulate=False):
|
||||
if self.scan_thread:
|
||||
return
|
||||
session = models.Session.objects.get(pk=sid)
|
||||
experiments = models.SessionExperiment.experiment_by_session(sid)
|
||||
|
||||
self.scan_thread = Thread(target=self._start_scanning, args=(session, experiments, simulate, ), daemon=True)
|
||||
self.scan_thread.start()
|
||||
|
||||
|
||||
def previous_well(self):
|
||||
@@ -316,22 +337,24 @@ class MultiWellManager:
|
||||
self.process._send(state='center', msg=msg)
|
||||
|
||||
self.cnc_controller.wait_for(0.1)
|
||||
logger.info("Fin du centrage")
|
||||
logger.info("Fin du test")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
logger.error("Error during scanning test", e)
|
||||
finally:
|
||||
self.test_thread = None
|
||||
|
||||
self.well_iterator.reset()
|
||||
self.process.cam._aligner.debug = False
|
||||
|
||||
logger.info(f"Scan terminé — retour à l'origine (X=0, Y=0) en {int(time.monotonic()-start_test)} s")
|
||||
self.cnc_controller.move_to(0, 0, feed=self.multiwell.feed*2)
|
||||
self.test_thread = None
|
||||
|
||||
|
||||
def scan_test(self, auto=False):
|
||||
if self.test_thread:
|
||||
return
|
||||
self.test_thread = Thread(target=self._scanning_test, args=(auto, ), daemon=True).start()
|
||||
self.test_thread = Thread(target=self._scanning_test, args=(auto, ), daemon=True)
|
||||
self.test_thread.start()
|
||||
|
||||
|
||||
@property
|
||||
|
||||
@@ -27,7 +27,7 @@ from modules import reductstore, grbl, utils, planarian_metrics
|
||||
from modules.circular_crop import CircularCrop, CropStrategy
|
||||
from .multiwell import MultiWellManager
|
||||
from .constants import ScannerConstants
|
||||
#from . import models
|
||||
from . import models
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -248,10 +248,12 @@ class ScannerProcess(Task):
|
||||
self.stop_event.set()
|
||||
|
||||
def start_services(self):
|
||||
Thread(target=self._listen_to_redis, daemon=True).start()
|
||||
Thread(target=self._init_manager, daemon=True).start()
|
||||
Thread(target=self._recording, daemon=True).start()
|
||||
Thread(target=self._init_grbl, daemon=True).start()
|
||||
Thread(target=self._listen_to_redis, daemon=True).start()
|
||||
self.cam.start()
|
||||
|
||||
logger.warning(f"Scanner services started ...")
|
||||
|
||||
def _send(self, **payload):
|
||||
@@ -267,20 +269,21 @@ class ScannerProcess(Task):
|
||||
self._send(**msg)
|
||||
|
||||
|
||||
def _store_metrics(self, uuid, metrics, ts):
|
||||
def _store_metric(self, uuid, metrics, ts):
|
||||
|
||||
if not isinstance(metrics, list):
|
||||
return
|
||||
for r in metrics:
|
||||
pid = r["planarian_id"]
|
||||
pid = r["planarian_id"]
|
||||
record = self.cam._metrics[pid].update(r, well_radius_mm=self.cam._params.well_radius_mm)
|
||||
logger.warning(f"{record}")
|
||||
|
||||
async_to_sync(planarianDB.store_metrics)(
|
||||
|
||||
async_to_sync(planarianDB.store_metric)(
|
||||
record,
|
||||
self.cam._params.experiment,
|
||||
self.cam._params.well_name,
|
||||
self.cam._params.well,
|
||||
entry_name=uuid,
|
||||
planarian=pid,
|
||||
record_type='metrics',
|
||||
ts_us=ts,
|
||||
)
|
||||
|
||||
def _store_frame(self, uuid, frame, ts, frame_count):
|
||||
@@ -292,15 +295,14 @@ class ScannerProcess(Task):
|
||||
self.recordDB.write(uuid, frame, ts=ts, labels=labels)
|
||||
|
||||
def _on_frame(self, jpeg_bytes: bytes, ts: datetime, metrics: dict, frame_count: int = 0) -> None:
|
||||
|
||||
if self.data.record:
|
||||
self.record_queue.put((self.data.uuid, ts, jpeg_bytes, metrics, frame_count))
|
||||
if self.data.play:
|
||||
try:
|
||||
jpeg=base64.b64encode(jpeg_bytes).decode()
|
||||
self._send(ts=ts.timestamp(), jpeg=jpeg, frame_count=frame_count)
|
||||
except Exception as e:
|
||||
logger.error(f"----_on_frame: {e}")
|
||||
try:
|
||||
if self.data.record:
|
||||
self.record_queue.put((self.data.uuid, ts, jpeg_bytes, metrics, frame_count))
|
||||
if self.data.play:
|
||||
jpeg=base64.b64encode(jpeg_bytes).decode()
|
||||
self._send(ts=ts.timestamp(), jpeg=jpeg, frame_count=frame_count)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
|
||||
def _recording(self):
|
||||
@@ -308,8 +310,11 @@ class ScannerProcess(Task):
|
||||
while not self.stop_event.is_set():
|
||||
try:
|
||||
(uuid, ts, frame, metrics, frame_count) = self.record_queue.get()
|
||||
self._store_metrics(uuid, metrics, ts)
|
||||
self._frame_store(uuid, frame, ts, frame_count)
|
||||
if self.cam.use_tracking:
|
||||
self._store_metric(uuid, metrics, ts)
|
||||
|
||||
self._store_frame(uuid, frame, ts, frame_count)
|
||||
|
||||
self.record_queue.task_done()
|
||||
except Exception as e:
|
||||
logger.error(f'recorder: {e}')
|
||||
@@ -324,15 +329,19 @@ class ScannerProcess(Task):
|
||||
self.grbl.wait_for(2.0)
|
||||
|
||||
|
||||
def _init_manager(self):
|
||||
logger.info(f"==== Start MultiWellManager!")
|
||||
self.manager = MultiWellManager(process=self)
|
||||
self.manager.set_calib_debug(False)
|
||||
Event().wait(2.0)
|
||||
|
||||
|
||||
def _listen_to_redis(self):
|
||||
try:
|
||||
logger.info(f"==== Scanner {self.group}: listen via redisDB")
|
||||
pubsub = redisDB.pubsub()
|
||||
pubsub.subscribe(self.group)
|
||||
#self._init_grbl()
|
||||
self.manager = MultiWellManager(process=self)
|
||||
self.manager.set_calib_debug(False)
|
||||
|
||||
|
||||
for message in pubsub.listen():
|
||||
try:
|
||||
#logger.info(f"{message}")
|
||||
@@ -356,12 +365,22 @@ class ScannerProcess(Task):
|
||||
elif topic == 'scan' or topic == 'simulate':
|
||||
logger.info(f"==== Scan {cmd}")
|
||||
sid = cmd.get("session", '0')
|
||||
|
||||
if sid == "0":
|
||||
self._send(state='error', msg=str(_('La session est nulle!...')))
|
||||
else:
|
||||
self.cam._active_median = False
|
||||
self.manager.scanning(sid, simulate=(topic=='simulate'))
|
||||
self._send(state=topic, msg=str(_('Balayage démarré...')))
|
||||
|
||||
try:
|
||||
self.cam._active_median = False
|
||||
simulate = (topic=='simulate')
|
||||
self.manager.scan_process(sid, simulate)
|
||||
|
||||
self._send(state=topic, msg=str(_('Balayage démarré...')))
|
||||
except Exception as e:
|
||||
logger.error(f"Scan error: {e}")
|
||||
self._send(state='error', msg=str(_('Erreur lors du démarrage du balayage...')))
|
||||
|
||||
continue
|
||||
|
||||
elif cmd["type"]=="calibrate":
|
||||
topic = cmd.get("topic")
|
||||
|
||||
@@ -23,6 +23,7 @@ class ScannerManager {
|
||||
this.area_px = options.area_px;
|
||||
this.frame_count = options.frame_count;
|
||||
this.scan_state = options.scan_state;
|
||||
this.sim_bt = options.simulate;
|
||||
}
|
||||
|
||||
init_controls() {
|
||||
@@ -30,6 +31,7 @@ class ScannerManager {
|
||||
this.crop.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "crop" }); });
|
||||
this.scan_bt.addEventListener('click', (e) => { this.scan(); });
|
||||
this.halt_bt.addEventListener('click', (e) => { this.halt(); });
|
||||
this.sim_bt.addEventListener('click', (e) => { this.simulate(); });
|
||||
}
|
||||
|
||||
registerSocket(socket) {
|
||||
@@ -58,6 +60,7 @@ class ScannerManager {
|
||||
|
||||
init() { this.axes = 0; this.cropping = 1; this._send({ type: 'scanner', topic: "init", }); }
|
||||
scan() { this._send({ type: 'scanner', topic: "scan", session: this.session.value ? this.session.value: "0" }); }
|
||||
simulate() { this._send({ type: 'scanner', topic: "simulate", session: this.session.value ? this.session.value: "0" }); }
|
||||
halt() { this._send({ type: 'calibrate', topic: "halt" }); }
|
||||
|
||||
_send(message) { this.socket.send(message); }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# tasks.py
|
||||
import asyncio
|
||||
import json
|
||||
import requests
|
||||
from celery import shared_task, group, chord, chain
|
||||
from celery.utils.log import get_task_logger
|
||||
from django.utils import timezone
|
||||
@@ -214,4 +215,64 @@ def on_scanning_done(result: dict, session_id: str):
|
||||
session.save(update_fields=["scanning_status", "scanning_finished_at"])
|
||||
|
||||
logger.info("==== Scanning done: session %s", session_id)
|
||||
|
||||
|
||||
@shared_task
|
||||
def http_request(req_type='json_post', url=None, user=None, passwd=None, headers={}, **kwargs):
|
||||
|
||||
def json_post():
|
||||
return requests.post(url, json=kwargs, headers=headers, auth=(user, passwd), timeout=3)
|
||||
|
||||
def post():
|
||||
return requests.post(url, params=kwargs, headers=headers, auth=(user, passwd), timeout=3)
|
||||
|
||||
def get():
|
||||
return requests.get(url, params=kwargs, headers=headers, auth=(user, passwd), timeout=3)
|
||||
|
||||
request = {
|
||||
'json_post': json_post,
|
||||
'post': post,
|
||||
'get':get
|
||||
}
|
||||
retries = 3
|
||||
for attempt in range(retries):
|
||||
try:
|
||||
response = request[req_type]()
|
||||
response.raise_for_status()
|
||||
try:
|
||||
data = response.json()
|
||||
except ValueError:
|
||||
data = response.text
|
||||
|
||||
resp = {"success": True, "data": data}
|
||||
logger.info(data)
|
||||
return resp
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
logger.error(f"{attempt + 1}/{retries}: Délai d'attente dépassé")
|
||||
except requests.exceptions.ConnectionError:
|
||||
logger.error(f"{attempt + 1}/{retries}: Erreur de connexion")
|
||||
except requests.exceptions.HTTPError as e:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur inattendue: {str(e)}")
|
||||
|
||||
@shared_task
|
||||
def supervisor_restart_service(params):
|
||||
"""
|
||||
Redémarre un service.
|
||||
|
||||
"""
|
||||
try:
|
||||
logger.warning("==== Supervisor scanner restart all services")
|
||||
http_request(
|
||||
req_type='get',
|
||||
url=f'http://localhost:9001/{params}',
|
||||
user='root',
|
||||
passwd='toor',
|
||||
headers={}
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"supervisor_restart_all_services error {e}")
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
</a>
|
||||
<a href="{% url 'scanner:logs_worker' %}" class="w3-bar-item w3-btn w3-hover-opacity w3-margin-left" target="_blank">
|
||||
<i class="fa-regular fa-file-lines w3-text-amber"></i> {% trans "Logs des services" %}
|
||||
</a>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -38,13 +38,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="w3-row w3-row-padding">
|
||||
<div class="w3-twothird">
|
||||
<div class="w3-twothird w3-center">
|
||||
<button id="_scan" class="w3-button w3-light-blue w3-round-large w3-margin-top w3-block">
|
||||
{% trans 'Lancer' %}<br>{% trans 'le balayage' %}<br><i class="fa-solid fa-circle-check w3-text-blue w3-xlarge"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w3-third">
|
||||
<button id="_scan" class="w3-button w3-dark-xlight w3-round-large w3-margin-top w3-block">
|
||||
<div class="w3-third w3-center">
|
||||
<button id="_simulate" class="w3-button w3-dark-xlight w3-round-large w3-margin-top w3-block">
|
||||
{% trans 'Simuler' %}<br>{% trans 'le balayage' %}<br><i class="fa-solid fa-triangle-exclamation w3-text-orange w3-xlarge"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -19,5 +19,6 @@ urlpatterns = [
|
||||
path('replay/', views.replay_view, name='replay'),
|
||||
path('api/stats/', views.stats_view, name='api_stats'),
|
||||
path('api/video/', views.download_api, name='download_api'),
|
||||
path('api/restart/', views.restart_all, name='restart_all'),
|
||||
path('api/export/', views.export_api, name='export_api'),
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
from asgiref.sync import async_to_sync
|
||||
import base64, json
|
||||
from django.shortcuts import render
|
||||
from django.shortcuts import render, redirect
|
||||
from django.http import JsonResponse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.decorators.http import require_GET, require_POST
|
||||
@@ -12,7 +12,7 @@ from reduct.time import unix_timestamp_to_iso
|
||||
from modules.system_stats import get_cached_stats, start_background_updater
|
||||
from modules import reductstore
|
||||
|
||||
from .tasks import download_video, export_all_images, export_all_videos
|
||||
from .tasks import download_video, export_all_images, export_all_videos, supervisor_restart_service
|
||||
from .process import CameraRecordManager, cameraDB
|
||||
from . import models
|
||||
from .constants import ScannerConstants
|
||||
@@ -157,6 +157,17 @@ def images_view(request):
|
||||
|
||||
|
||||
## replay
|
||||
@require_GET
|
||||
@csrf_exempt
|
||||
def restart_all(request):
|
||||
#http://scanner.local:9001/index.html?processname=test_tube%3Aservices&action=restart
|
||||
#http://scanner.local:9001/index.html?processname=test_tube%3AwebUI&action=start
|
||||
#http://scanner.local:9001/index.html?action=restartall
|
||||
#supervisor_restart_service('index.html?action=restartall')
|
||||
supervisor_restart_service('index.html?processname=test_tube%3Aservices&action=restart')
|
||||
return JsonResponse({"state": True})
|
||||
|
||||
|
||||
@require_POST
|
||||
@csrf_exempt
|
||||
def download_api(request):
|
||||
@@ -167,8 +178,8 @@ def download_api(request):
|
||||
return download_video.delay(uuid, dt_start, dt_stop, frame_rate=frame_rate) # @UndefinedVariable
|
||||
else:
|
||||
return JsonResponse({"state": False})
|
||||
|
||||
|
||||
|
||||
|
||||
def get_video(uuid):
|
||||
oldest, latest = async_to_sync(reductstore.old_last_dates)(cameraDB, entry_name=uuid)
|
||||
filters = record_manager.set_filters(test=False)
|
||||
|
||||
Reference in New Issue
Block a user