calibrage auto
This commit is contained in:
@@ -1,15 +1,20 @@
|
||||
'''
|
||||
scanner/multiwell.py
|
||||
WellIterator: Itérateur personnalisé pour naviguer dans les Wells
|
||||
MultiWellManager: Manager des multi-puits
|
||||
|
||||
Created on 20 avr. 2026
|
||||
|
||||
@author: denis
|
||||
'''
|
||||
import cv2
|
||||
import numpy as np
|
||||
import logging
|
||||
import time
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from threading import Thread, Event
|
||||
#from django.utils.translation import gettext_lazy as _
|
||||
from django.utils import timezone
|
||||
from django.utils.html import mark_safe
|
||||
from modules import grbl
|
||||
from . import models
|
||||
|
||||
|
||||
@@ -74,14 +79,12 @@ class MultiWellManager:
|
||||
self.process = process
|
||||
self.cnc_controller = process.grbl
|
||||
self.stop_playing = Event()
|
||||
self.well_iterator = None
|
||||
|
||||
self.scanner = None
|
||||
|
||||
self.well_iterator = None
|
||||
self.multiwel = None
|
||||
self.set_default_values()
|
||||
self.set_multiwell()
|
||||
|
||||
self.scan_thread = None
|
||||
self.test_thread = None
|
||||
|
||||
def set_default_values(self, feed=None, step=None, duration=None):
|
||||
self._feed = feed or self.process.conf.calibration_default_feed
|
||||
@@ -103,6 +106,7 @@ class MultiWellManager:
|
||||
self._ybase = self.multiwell.ybase
|
||||
self._dx = self.multiwell.dx
|
||||
self._dy = self.multiwell.dy
|
||||
|
||||
return self.multiwell.config()
|
||||
|
||||
|
||||
@@ -176,14 +180,18 @@ class MultiWellManager:
|
||||
|
||||
def halt_scanning(self):
|
||||
self.process.data.record = False
|
||||
return self.stop_playing.set()
|
||||
|
||||
self.stop_playing.set()
|
||||
self.scan_thread = None
|
||||
self.test_thread = None
|
||||
self.well_iterator.reset()
|
||||
|
||||
|
||||
def scanning(self, sid):
|
||||
try:
|
||||
session = models.Session.objects.get(pk=sid)
|
||||
observations = models.SessionObservation.observation_by_session(sid)
|
||||
Thread(target=self._start_scanning, args=(session, observations, ), daemon=True).start()
|
||||
if self.scan_thread is None:
|
||||
session = models.Session.objects.get(pk=sid)
|
||||
observations = models.SessionObservation.observation_by_session(sid)
|
||||
self.scan_thread = Thread(target=self._start_scanning, args=(session, observations, ), daemon=True).start()
|
||||
except Exception as e:
|
||||
print("MultiWellManager::scan error", e)
|
||||
|
||||
@@ -208,34 +216,62 @@ class MultiWellManager:
|
||||
|
||||
def set_well_position(self):
|
||||
w = self.well_iterator.get_current()
|
||||
w.x, w.y = self.cnc_controller.get_mpos()
|
||||
w.save()
|
||||
return {"state": "well_position", "msg": f">>> saved ({w.x}, {w.y})"}
|
||||
if w:
|
||||
w.x, w.y = self.cnc_controller.get_mpos()
|
||||
w.save()
|
||||
if w.order == 0:
|
||||
models.MultiWell.objects.filter(position__exact=w.multiwell.position).update(xbase=w.x, ybase=w.y)
|
||||
return {"state": "well_position", "msg": f">>> saved ({w.x}, {w.y})"}
|
||||
return {"state": "well_position", "msg": f">>> pas de puit"}
|
||||
|
||||
|
||||
def _scanning_test(self, xnext=0, ynext=0):
|
||||
def _scanning_test(self, auto=False):
|
||||
self.stop_playing = Event()
|
||||
self.process.data.tube_diameter = self.multiwell.diameter
|
||||
cam = self.process.cam
|
||||
duration = self.duration if not auto else 45.0
|
||||
start_test = time.monotonic()
|
||||
|
||||
for w in self.well_iterator:
|
||||
if self.stop_playing.is_set():
|
||||
break
|
||||
self.cnc_controller.wait_for(2.0)
|
||||
self.cnc_controller.move_to(w.x, w.y, feed=w.multiwell.feed)
|
||||
|
||||
self.process._send(current=w.order)
|
||||
|
||||
start = time.monotonic()
|
||||
while not self.stop_playing.is_set():
|
||||
if time.monotonic() - start > self.duration:
|
||||
if time.monotonic() - start > duration:
|
||||
break
|
||||
self.cnc_controller.wait_for(1.0)
|
||||
|
||||
logger.info(f"Arrêter la simulation")
|
||||
|
||||
if auto and cam._last_detection:
|
||||
if cam._last_detection.get('action')=="grbl":
|
||||
self.cnc_controller.wait_for(5.0)
|
||||
dx_mm = cam._last_detection["offset_x_mm"]
|
||||
dy_mm = cam._last_detection["offset_y_mm"]
|
||||
|
||||
self.cnc_controller.move_to(self.cnc_controller.x + dx_mm, self.cnc_controller.y + dy_mm, feed=150)
|
||||
msg = f"Correction CNC move_relative(dx={dx_mm:.3f}, dy={dy_mm:.3f})"
|
||||
self.process._send(state='center', msg=msg)
|
||||
elif cam._last_detection.get('action') in ['none', 'crop']:
|
||||
msg = f"ok {w.multiwell.position}-{w.well.name}: ({self.cnc_controller.x}, {self.cnc_controller.y})"
|
||||
logger.info(msg)
|
||||
self.process._send(state='save', msg=msg)
|
||||
w.x, w.y = self.cnc_controller.x, self.cnc_controller.y
|
||||
w.save()
|
||||
if w.order == 0:
|
||||
models.MultiWell.objects.filter(position__exact=self.position).update(xbase=w.x, ybase=w.y)
|
||||
break
|
||||
self.cnc_controller.wait_for(0.1)
|
||||
logger.info("Fin du centrage")
|
||||
self.well_iterator.reset()
|
||||
|
||||
logger.info(f"Scan terminé — retour à l'origine (X={xnext:.1f} Y={ynext:.1f})")
|
||||
self.cnc_controller.move_to(xnext, ynext, feed=self.multiwell.feed*2)
|
||||
logger.info(f"Scan terminé — retour à l'origine (X=0, Y=0) en {time.monotonic()-start_test} s")
|
||||
self.cnc_controller.move_to(0, 0, feed=self.multiwell.feed*2)
|
||||
|
||||
|
||||
def scan_test(self):
|
||||
Thread(target=self._scanning_test, daemon=True).start()
|
||||
def scan_test(self, auto=False):
|
||||
if self.test_thread is None:
|
||||
self.test_thread = Thread(target=self._scanning_test, args=(auto, ), daemon=True).start()
|
||||
|
||||
|
||||
@property
|
||||
@@ -303,8 +339,11 @@ class MultiWellManager:
|
||||
self._dy = value
|
||||
|
||||
|
||||
def set_xy_step(self):
|
||||
models.MultiWell.objects.filter(position__exact=self.position).update(dx=self.dx, dy=self.dy)
|
||||
def get_well_order(self):
|
||||
w = self.well_iterator.get_current()
|
||||
if w:
|
||||
return w.order
|
||||
return None
|
||||
|
||||
|
||||
def set_position(self):
|
||||
@@ -312,10 +351,21 @@ class MultiWellManager:
|
||||
self.cnc_controller.wait_for(2.0)
|
||||
models.MultiWell.objects.filter(position__exact=self.position).update(xbase=x, ybase=y)
|
||||
self._xbase, self._ybase = x, y
|
||||
w = self.well_iterator.seek(0) # base puit 0
|
||||
w.x, w.y = x, y
|
||||
w.save()
|
||||
|
||||
|
||||
def calib_toggle_debug(self):
|
||||
""" Active / désactive le mode debug sur le stream."""
|
||||
aligner = self.process.cam._aligner
|
||||
aligner.debug = not aligner.debug
|
||||
return {"state": "debug", "msg": f"Debug: {aligner.debug}"}
|
||||
return {"state": "debug", "msg": f"Debug: {aligner.debug}"}
|
||||
|
||||
def set_calib_debug(self, value=True):
|
||||
""" Active / désactive le mode debug sur le stream."""
|
||||
aligner = self.process.cam._aligner
|
||||
aligner.debug = value
|
||||
return {"state": "debug", "msg": f"Debug: {aligner.debug}"}
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import os
|
||||
os.environ['OPENCV_LOG_LEVEL']="0"
|
||||
os.environ['OPENCV_FFMPEG_LOGLEVEL']="0"
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from datetime import datetime
|
||||
@@ -36,7 +35,8 @@ class ProcessData:
|
||||
record: bool = False
|
||||
uuid: str = None
|
||||
session: int = 0
|
||||
frame: bytes = None
|
||||
tube_diameter: float = 16.0
|
||||
|
||||
|
||||
logger = get_task_logger(__name__)
|
||||
redisDB = Redis(host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=0, decode_responses=True)
|
||||
@@ -148,7 +148,7 @@ class ScannerProcess(Task):
|
||||
self.cam = None
|
||||
self.grbl = None
|
||||
self.crop = None
|
||||
self.multiwel = None
|
||||
#self.multiwel = None
|
||||
self.conf = None
|
||||
self.record_queue = Queue()
|
||||
self.data = ProcessData()
|
||||
@@ -187,9 +187,9 @@ class ScannerProcess(Task):
|
||||
height=self.video_height,
|
||||
jpeg_quality=self.video_quality,
|
||||
use_tracking=self.use_tracking,
|
||||
px_per_mm = self.conf.px_per_mm,
|
||||
display=self._display,
|
||||
video_lists=[],
|
||||
parent=self,
|
||||
)
|
||||
'''
|
||||
settings.MEDIA_ROOT / 'simulation' / 'part1-5fps.mp4',
|
||||
@@ -209,8 +209,8 @@ class ScannerProcess(Task):
|
||||
height=self.video_height,
|
||||
jpeg_quality=self.video_quality,
|
||||
use_tracking=self.use_tracking,
|
||||
px_per_mm = self.conf.px_per_mm,
|
||||
display=self._display,
|
||||
parent=self,
|
||||
)
|
||||
else:
|
||||
from modules.picamera2_capture import PiCamera2Capture
|
||||
@@ -220,8 +220,8 @@ class ScannerProcess(Task):
|
||||
height=self.video_height,
|
||||
jpeg_quality=self.video_quality,
|
||||
use_tracking=self.use_tracking,
|
||||
px_per_mm = self.conf.px_per_mm,
|
||||
display=self._display,
|
||||
parent=self,
|
||||
)
|
||||
|
||||
self.cam.set_frame_callback(self._on_frame)
|
||||
@@ -263,12 +263,9 @@ class ScannerProcess(Task):
|
||||
self._send(**msg)
|
||||
|
||||
def _on_frame(self, jpeg_bytes: bytes, ts: datetime, metrics: dict) -> None:
|
||||
self.data.frame = jpeg_bytes
|
||||
if self.data.record:
|
||||
# record images
|
||||
self.record_queue.put((self.data.uuid, ts, jpeg_bytes, metrics))
|
||||
if self.data.play:
|
||||
# play image
|
||||
self._send(ts=ts.timestamp(), jpeg=base64.b64encode(jpeg_bytes).decode(), **metrics)
|
||||
|
||||
def _recording(self):
|
||||
@@ -399,37 +396,32 @@ class ScannerProcess(Task):
|
||||
|
||||
elif topic == 'goto_xy':
|
||||
self.grbl.move_to(self.manager.xbase, self.manager.ybase, feed=self.manager.feed)
|
||||
self.manager.well_iterator.reset()
|
||||
self.manager.well_iterator.seek(0)
|
||||
|
||||
elif topic == 'xy_base':
|
||||
self.manager.set_position()
|
||||
|
||||
elif topic == 'dx':
|
||||
self.manager.dx = float(value)
|
||||
|
||||
elif topic == 'dy':
|
||||
self.manager.dy = float(value)
|
||||
|
||||
elif topic == 'xy_step':
|
||||
self.manager.set_xy_step()
|
||||
|
||||
elif topic == 'test':
|
||||
pass
|
||||
#self.manager.scan_test()
|
||||
self.manager.scan_test()
|
||||
continue
|
||||
|
||||
elif topic == 'auto':
|
||||
self.manager.set_calib_debug(True)
|
||||
self.cam.set_circular_crop(self.crop)
|
||||
self.manager.scan_test(auto=True)
|
||||
continue
|
||||
|
||||
elif topic == 'center':
|
||||
#self.manager.scan_test()
|
||||
dx_mm = self.cam._last_detection["offset_x_mm"]
|
||||
dy_mm = self.cam._last_detection["offset_y_mm"]
|
||||
self.grbl.move_to(self.grbl.x + dx_mm, self.grbl.y + dy_mm, feed=150)
|
||||
|
||||
msg = f"Correction CNC move_relative(dx={dx_mm:.3f}, dy={dy_mm:.3f})"
|
||||
self._send(state='center', msg=msg)
|
||||
continue
|
||||
|
||||
elif topic == 'halt':
|
||||
self.manager.halt_scanning()
|
||||
continue
|
||||
|
||||
|
||||
elif topic == 'calib_debug':
|
||||
msg = self.manager.calib_toggle_debug()
|
||||
@@ -439,23 +431,19 @@ class ScannerProcess(Task):
|
||||
elif topic == 'previous':
|
||||
msg = self.manager.previous_well()
|
||||
self._send(**msg)
|
||||
continue
|
||||
|
||||
|
||||
elif topic == 'next':
|
||||
msg = self.manager.next_well()
|
||||
self._send(**msg)
|
||||
continue
|
||||
|
||||
|
||||
elif topic == 'goto':
|
||||
msg = self.manager.goto_well(int(value))
|
||||
self._send(**msg)
|
||||
continue
|
||||
|
||||
|
||||
elif topic == 'set_well':
|
||||
msg = self.manager.set_well_position()
|
||||
self._send(**msg)
|
||||
continue
|
||||
|
||||
|
||||
self._send(
|
||||
xbase=self.manager.xbase,
|
||||
ybase=self.manager.ybase,
|
||||
@@ -466,8 +454,8 @@ class ScannerProcess(Task):
|
||||
dx=self.manager.dx,
|
||||
dy=self.manager.dy,
|
||||
buttons=buttons,
|
||||
current=self.manager.get_well_order(),
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f'scanner listen_to_redis: {e}')
|
||||
finally:
|
||||
|
||||
@@ -1,88 +1,76 @@
|
||||
|
||||
class ScannerManager {
|
||||
|
||||
constructor(container) {
|
||||
constructor(container, options = {}) {
|
||||
this.container = container;
|
||||
this.socket = null;
|
||||
this.axes = 0;
|
||||
this.cropping = 0;
|
||||
this.debug_count = 0
|
||||
this.debug_count = 0;
|
||||
|
||||
this.ts = options.ts;
|
||||
this.cx = options.cx;
|
||||
this.cy = options.cy;
|
||||
this.x = options.x;
|
||||
this.y = options.y;
|
||||
this.xbase= options.xbase;
|
||||
this.ybase= options.ybase;
|
||||
this.test = options.test;
|
||||
this.halt = options.halt;
|
||||
this.speed_px_s = options.speed_px_s;
|
||||
this.axial_speed = options.axial_speed;
|
||||
this.axial_pos = options.axial_pos;
|
||||
this.area_px = options.area_px;
|
||||
this.frame_count = options.frame_count;
|
||||
this.goto_0 = options.goto_0;
|
||||
this.goto_xy = options.goto_xy;
|
||||
this.xy_base = options.xy_base;
|
||||
this.up = options.up;
|
||||
this.down = options.down;
|
||||
this.left = options.left;
|
||||
this.right = options.right;
|
||||
this.duration= options.duration;
|
||||
this.feed = options.feed;
|
||||
this.step = options.step;
|
||||
this.well = options.well;
|
||||
this.debug = options.debug;
|
||||
this.calib_debug = options.calib_debug;
|
||||
this.calib_center= options.calib_center;
|
||||
this.previous = options.previous;
|
||||
this.next = options.next;
|
||||
this.set_well = options.set_well;
|
||||
this.well_btn = options.well_btn;
|
||||
this.median = options.median;
|
||||
this.crop = options.crop;
|
||||
this.crop_radius = options.crop_radius;
|
||||
this.calib_auto = options.calib_auto;
|
||||
}
|
||||
|
||||
init_controls() {
|
||||
this.up.addEventListener('mousedown', (e) => { this._send({ type: 'calibrate', topic: "up" }); });
|
||||
this.down.addEventListener('mousedown', (e) => { this._send({ type: 'calibrate', topic: "down" }); });
|
||||
this.left.addEventListener('mousedown', (e) => { this._send({ type: 'calibrate', topic: "left" }); });
|
||||
this.right.addEventListener('mousedown', (e) => { this._send({ type: 'calibrate', topic: "right" }); });
|
||||
|
||||
init_controls() {
|
||||
this.ts = sId("_ts");
|
||||
this.cx = sId("_cx");
|
||||
this.cy = sId("_cy");
|
||||
this.speed_px_s = sId("_speed_px_s");
|
||||
this.axial_speed = sId("_axial_speed");
|
||||
this.axial_pos = sId("_axial_pos");
|
||||
this.area_px = sId("_area_px");
|
||||
this.frame_count = sId("_count");
|
||||
this.goto_0.addEventListener('click', (e) => { this.clear_buttons(); this._send({ type: 'calibrate', topic: "goto_0" }); });
|
||||
this.goto_xy.addEventListener('click', (e) => { this.clear_buttons(); this._send({ type: 'calibrate', topic: "goto_xy" }); });
|
||||
this.xy_base.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "xy_base" }); });
|
||||
|
||||
const goto_0 = sId("_goto-0");
|
||||
const goto_xy = sId("_goto-xy");
|
||||
const xy_base = sId("_xy-base");
|
||||
const xy_step = sId("_xy-step");
|
||||
const up = sId("_up");
|
||||
const down = sId("_down");
|
||||
const left = sId("_left");
|
||||
const right = sId("_right");
|
||||
this.duration = sId("_duration");
|
||||
this.feed = sId("_feed");
|
||||
this.step = sId("_step");
|
||||
this.well = sId("_well");
|
||||
this.x = sId("_x");
|
||||
this.y = sId("_y");
|
||||
this.dx = sId("_dx");
|
||||
this.dy = sId("_dy");
|
||||
this.xbase = sId("_xbase");
|
||||
this.ybase = sId("_ybase");
|
||||
this.debug = sId("_debug");
|
||||
this.well_btn = sId("_well_btn");
|
||||
|
||||
const test = sId("_test");
|
||||
const halt = sId("_halt");
|
||||
|
||||
const calib_debug = sId("_calib_debug");
|
||||
const calib_center = sId("_calib_center");
|
||||
const previous = sId("_previous");
|
||||
const next = sId("_next");
|
||||
const set_well = sId("_set_well");
|
||||
|
||||
const median = sId("_median");
|
||||
const crop = sId("_crop");
|
||||
const crop_radius = sId("_crop_radius");
|
||||
this.calib_debug.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "calib_debug" }); });
|
||||
this.previous.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "previous" }); });
|
||||
this.next.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "next" }); });
|
||||
this.set_well.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "set_well" }); });
|
||||
|
||||
this.median.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "median" }); });
|
||||
this.crop.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "crop" }); });
|
||||
this.crop_radius.addEventListener('change',(e) => { this._send({ type: 'calibrate', topic: "crop_radius", value: this.crop_radius.value }); });
|
||||
this.well.addEventListener("change", (e) => { this._send({ type: 'calibrate', topic: "position", value: e.target.value }); });
|
||||
this.step.addEventListener("change", (e) => { this._send({ type: 'calibrate', topic: "step", value: e.target.value }); });
|
||||
this.feed.addEventListener("change", (e) => { this._send({ type: 'calibrate', topic: "feed", value: e.target.value }); });
|
||||
this.duration.addEventListener("change", (e) => { this._send({ type: 'calibrate', topic: "duration", value: e.target.value }); });
|
||||
|
||||
up.addEventListener('mousedown', (e) => { this._send({ type: 'calibrate', topic: "up" }); });
|
||||
down.addEventListener('mousedown', (e) => { this._send({ type: 'calibrate', topic: "down" }); });
|
||||
left.addEventListener('mousedown', (e) => { this._send({ type: 'calibrate', topic: "left" }); });
|
||||
right.addEventListener('mousedown', (e) => { this._send({ type: 'calibrate', topic: "right" }); });
|
||||
|
||||
goto_0.addEventListener('click', (e) => { this.clear_buttons(); this._send({ type: 'calibrate', topic: "goto_0" }); });
|
||||
goto_xy.addEventListener('click', (e) => { this.clear_buttons(); this._send({ type: 'calibrate', topic: "goto_xy" }); });
|
||||
xy_base.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "xy_base" }); });
|
||||
xy_step.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "xy_step" }); });
|
||||
|
||||
calib_debug.addEventListener('click',(e) => { this._send({ type: 'calibrate', topic: "calib_debug" }); });
|
||||
previous.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "previous" }); });
|
||||
next.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "next" }); });
|
||||
set_well.addEventListener('click',(e) => { this._send({ type: 'calibrate', topic: "set_well" }); });
|
||||
|
||||
median.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "median" }); });
|
||||
crop.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "crop" }); });
|
||||
crop_radius.addEventListener('change',(e) => { this._send({ type: 'calibrate', topic: "crop_radius", value: crop_radius.value }); });
|
||||
this.well.addEventListener("change", (e) => { this._send({ type: 'calibrate', topic: "position", value: e.target.value }); });
|
||||
this.step.addEventListener("change", (e) => { this._send({ type: 'calibrate', topic: "step", value: e.target.value }); });
|
||||
this.feed.addEventListener("change", (e) => { this._send({ type: 'calibrate', topic: "feed", value: e.target.value }); });
|
||||
this.duration.addEventListener("change", (e) => { this._send({ type: 'calibrate', topic: "duration", value: e.target.value }); });
|
||||
|
||||
this.dx.addEventListener("change", (e) => { this._send({ type: 'calibrate', topic: "dx", value: e.target.value }); });
|
||||
this.dy.addEventListener("change", (e) => { this._send({ type: 'calibrate', topic: "dy", value: e.target.value }); });
|
||||
|
||||
test.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "test" }); });
|
||||
calib_center.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "center" }); });
|
||||
halt.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "halt" }); });
|
||||
this.test.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "test" }); });
|
||||
this.calib_center.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "center" }); });
|
||||
this.calib_auto.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "auto" }); });
|
||||
this.halt.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "halt" }); });
|
||||
}
|
||||
|
||||
registerSocket(socket) {
|
||||
@@ -91,10 +79,9 @@ class ScannerManager {
|
||||
}
|
||||
|
||||
update(payload) {
|
||||
try {
|
||||
try {
|
||||
if (payload.jpeg) { this.container.src = `data:image/jpeg;base64,${payload.jpeg}`; }
|
||||
if (payload.xbase) { this.xbase.textContent = payload.xbase; this.ybase.textContent = payload.ybase; }
|
||||
if (payload.dxy) { this.dy.value=payload.dy; this.dx.value=payload.dx; }
|
||||
if (payload.xy) { this.x.textContent=payload.x.toFixed(2); this.y.textContent=payload.y.toFixed(2); }
|
||||
if (payload.state) { this.debug.insertAdjacentHTML('afterbegin', `<li>[ ${++this.debug_count} - ${payload.state} ]: ${payload.msg}</li>`); }
|
||||
if (payload.ts) { this.ts.textContent = timestampToLocalISOString(payload.ts); }
|
||||
@@ -108,6 +95,12 @@ class ScannerManager {
|
||||
this.frame_count.textContent = payload.count;
|
||||
}
|
||||
if (payload.buttons) { this.well_btn.innerHTML = payload.buttons; }
|
||||
if (payload.current >= 0) {
|
||||
document.querySelectorAll('button.w3-button.well').forEach(btn => {
|
||||
if (btn.value==payload.current) { btn.classList.add('w3-green'); return; }
|
||||
btn.classList.remove('w3-green');
|
||||
});
|
||||
}
|
||||
|
||||
} catch(e) { console.log(e); }
|
||||
}
|
||||
@@ -115,8 +108,6 @@ class ScannerManager {
|
||||
clear_buttons() { document.querySelectorAll('button.w3-button.well').forEach(btn => {btn.classList.remove('w3-green'); }); }
|
||||
goto_well(b) { this.clear_buttons(); b.classList.add('w3-green'); this._send({ type: 'calibrate', topic: "goto", value: b.value }); }
|
||||
init() {
|
||||
this.axes = 0;
|
||||
this.cropping = 0;
|
||||
this.clear_buttons();
|
||||
this._send({
|
||||
type: 'calibrate',
|
||||
|
||||
@@ -1,40 +1,34 @@
|
||||
|
||||
class ScannerManager {
|
||||
|
||||
constructor(container) {
|
||||
constructor(container, options = {}) {
|
||||
this.container = container;
|
||||
this.socket = null;
|
||||
this.axes = 0;
|
||||
this.cropping = 1;
|
||||
this.debug_count = 0
|
||||
this.debug_count = 0;
|
||||
|
||||
this.ts = options.ts;
|
||||
this.cx = options.cx;
|
||||
this.cy = options.cy;
|
||||
this.x = options.x;
|
||||
this.y = options.y;
|
||||
this.session = options.session;
|
||||
this.scan_bt = options.scan;
|
||||
this.halt_bt = options.halt;
|
||||
this.debug = options.debug;
|
||||
this.median = options.median;
|
||||
this._rop = options.crop;
|
||||
this.speed_px_s = options.speed_px_s;
|
||||
this.axial_speed = options.axial_speed;
|
||||
this.axial_pos = options.axial_pos;
|
||||
this.area_px = options.area_px;
|
||||
this.frame_count = options.frame_count;
|
||||
}
|
||||
|
||||
toggle_median() { this.axes = !this.axes; return this.axes; }
|
||||
toggle_crop() { this.croping = !this.croping; return this.croping; }
|
||||
|
||||
init_controls() {
|
||||
this.cx = sId("_cx");
|
||||
this.cy = sId("_cy");
|
||||
this.speed_px_s = sId("_speed_px_s");
|
||||
this.axial_speed = sId("_axial_speed");
|
||||
this.axial_pos = sId("_axial_pos");
|
||||
this.area_px = sId("_area_px");
|
||||
this.frame_count = sId("_count");
|
||||
|
||||
this.session= sId("_session");
|
||||
this.ts = sId("_ts");
|
||||
this.x = sId("_x");
|
||||
this.y = sId("_y");
|
||||
this.debug = sId("_debug");
|
||||
const scan = sId("_scan");
|
||||
const halt = sId("_halt");
|
||||
const median = sId("_median");
|
||||
const crop = sId("_crop");
|
||||
|
||||
median.addEventListener('click',(e) => { this._send({ type: 'calibrate', topic: "median", value: this.toggle_median() }); });
|
||||
crop.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "crop", value: this.toggle_crop() }); });
|
||||
scan.addEventListener('click', (e) => { this.scan(); });
|
||||
halt.addEventListener('click', (e) => { this.halt(); });
|
||||
this.median.addEventListener('click',(e) => { this._send({ type: 'calibrate', topic: "median" }); });
|
||||
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(); });
|
||||
}
|
||||
|
||||
registerSocket(socket) {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="container w3-black">
|
||||
<div class="header">
|
||||
<div class="w3-row w3-row-padding">
|
||||
<div class="w3-col" style="width:15%">
|
||||
<div class="w3-col" style="width:20%">
|
||||
{% trans 'Position multi-puit' %}<br>
|
||||
<select id="_well" class="w3-select">
|
||||
{% for w in wells %}
|
||||
@@ -32,7 +32,7 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="w3-col" style="width:15%">
|
||||
<div class="w3-col" style="width:10%">
|
||||
<div>{% trans 'Pas' %}</div>
|
||||
<select id="_step" class="w3-select">
|
||||
<option value="0.1">0.1 mm</option>
|
||||
@@ -50,7 +50,7 @@
|
||||
<option value="50.0">50.0 mm</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="w3-col" style="width:15%">
|
||||
<div class="w3-col" style="width:10%">
|
||||
<div>{% trans 'Durée' %}</div>
|
||||
<select id="_duration" class="w3-select" title="{% trans 'Durée entre vidéos' %}">
|
||||
<option value="1.0">1 s</option>
|
||||
@@ -59,10 +59,13 @@
|
||||
<option value="4.0">4 s</option>
|
||||
<option value="5.0" selected>5 s</option>
|
||||
<option value="10">10 s</option>
|
||||
<option value="15">15 s</option>
|
||||
<option value="20">20 s</option>
|
||||
<option value="30">30 s</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="w3-col" style="width:40%">
|
||||
<div class="w3-margin-top w3-padding w3-right">
|
||||
<div class="w3-col" style="width:45%">
|
||||
<div class="w3-margin-top w3-padding w3-right-align">
|
||||
<span id="_ts"></span><br>
|
||||
</div>
|
||||
</div>
|
||||
@@ -70,77 +73,105 @@
|
||||
</div>
|
||||
|
||||
<div class="move w3-center">
|
||||
|
||||
<div class="w3-row">
|
||||
<div class="w3-half">{% trans 'dx' %}<br><input id="_dx" type="number" min="15.0" max="25.0" step="0.01" value=""/></div>
|
||||
<div class="w3-half">{% trans 'dy' %}<br><input id="_dy" type="number" min="15.0" max="25.0" step="0.01" value=""/></div>
|
||||
<div class="w3-col">
|
||||
<button id="_xy-step" class="w3-button w3-warning w3-round-large w3-block w3-large">
|
||||
<i class="fa-solid fa-left-right"></i> {% trans 'Définir (dx, dy)' %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div><button id="_up" class="w3-button w3-dark-xlight w3-round-large w3-margin-small w3-block">↑ +Y {% trans 'A Droite' %}</button></div>
|
||||
<div><button id="_left" class="w3-button w3-dark-xlight w3-round-large w3-margin-small w3-block">← -X {% trans 'En bas' %}</button></div>
|
||||
<div><button id="_right" class="w3-button w3-dark-xlight w3-round-large w3-margin-small w3-block">→ +X {% trans 'En haut' %}</button></div>
|
||||
<div><button id="_down" class="w3-button w3-dark-xlight w3-round-large w3-margin-small w3-block">↓ -Y {% trans 'A Gauche' %}</button></div>
|
||||
|
||||
<div class="w3-row">
|
||||
<div id="_well_btn" class="w3-col"></div>
|
||||
<div class="w3-half">
|
||||
<button id="_previous" class="w3-button w3-light-blue w3-round-large w3-block" title="{% trans 'Précédent' %}">
|
||||
<i class="fa-solid fa-circle-left w3-large"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w3-half">
|
||||
<button id="_next" class="w3-button w3-light-blue w3-round-large w3-block" title="{% trans 'Suivant' %}">
|
||||
<i class="fa-solid fa-circle-right w3-large"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w3-col">
|
||||
<button id="_set_well" class="w3-button w3-warning w3-round-large w3-block">
|
||||
<i class="fa-solid fa-circle-check"></i> {% trans 'Définir position' %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="w3-col">
|
||||
<button id="_calib_center" class="w3-button w3-warning w3-round-large w3-block">
|
||||
<i class="fa-regular fa-circle"></i> {% trans 'Centrer manuel' %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scan w3-center">
|
||||
<div class="w3-row">
|
||||
<div class="w3-row w3-border w3-bold w3-margin-bottom">
|
||||
<div class="w3-half">X<br><span id="_x"></span></div>
|
||||
<div class="w3-half">Y<br><span id="_y"></span></div>
|
||||
</div>
|
||||
<button id="_goto-0" class="w3-button w3-light-blue w3-round w3-round-large w3-margin-small w3-block">Origine (0, 0)</button>
|
||||
<button id="_goto-xy" class="w3-button w3-light-blue w3-round-large w3-margin-small w3-block w3-margin-bottom">
|
||||
{% trans 'Aller à la base' %}<br>(<span id="_xbase"></span>, <span id="_ybase"></span>)
|
||||
</div>
|
||||
<div class="w3-row">
|
||||
<div class="w3-col">
|
||||
<button id="_xy-base" class="w3-button w3-dark-xlight w3-round-large w3-margin-small w3-block"
|
||||
title="{% trans 'Définir la base (x, y) du multi-puit' %}">
|
||||
{% trans 'Définir base' %}<br><i class="fa-solid fa-circle-check w3-text-orange w3-xxlarge"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w3-col">
|
||||
<button id="_right" class="w3-button w3-dark-xlight w3-round-large w3-padding-small">
|
||||
+X<br><i class="fa-solid fa-circle-up w3-xxlarge"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w3-third">
|
||||
<button id="_down" class="w3-button w3-dark-xlight w3-round-large w3-padding-small">
|
||||
-Y<br><i class="fa-solid fa-circle-arrow-left w3-xxlarge"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w3-third">
|
||||
<button id="_set_well" class="w3-button w3-dark-xlight w3-round-large w3-padding-small"
|
||||
title="{% trans 'Sauvegarder la position du puit courant' %}">
|
||||
<span class="w3-small">Position</span><br><i class="fa-solid fa-circle-check w3-xxlarge w3-text-orange"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w3-third">
|
||||
<button id="_up" class="w3-button w3-dark-xlight w3-round-large w3-padding-small">
|
||||
+Y<br><i class="fa-solid fa-circle-arrow-right w3-xxlarge"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w3-col">
|
||||
<button id="_left" class="w3-button w3-dark-xlight w3-round-large w3-padding-small">
|
||||
-X<br><i class="fa-solid fa-circle-down w3-xxlarge"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w3-row">
|
||||
<div id="_well_btn" class="w3-col w3-margin-top w3-margin-bottom"></div>
|
||||
|
||||
<div class="w3-third">
|
||||
<button id="_previous" class="w3-button w3-dark-xlight w3-round-large w3-padding-small">
|
||||
<span class="w3-small">{% trans 'Précéd.' %}</span><br><i class="fa-solid fa-circle-left w3-xxlarge"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w3-third">
|
||||
<button id="_calib_center" class="w3-button w3-dark-xlight w3-round-large w3-padding-small" title="{% trans 'Centrer manuellement' %}">
|
||||
<span class="w3-small">{% trans 'Centrer' %}</span><br><i class="fa-regular fa-circle w3-xxlarge w3-text-orange"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w3-third">
|
||||
<button id="_next" class="w3-button w3-dark-xlight w3-round-large w3-padding-small">
|
||||
<span class="w3-small">{% trans 'Suivant' %}</span><br><i class="fa-solid fa-circle-right w3-xxlarge"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w3-col">
|
||||
<button id="_calib_auto" class="w3-button w3-dark-xlight w3-round-large w3-margin-small w3-block"
|
||||
title="{% trans 'Centrage auto et sauvegarde des positions' %}">
|
||||
{% trans 'Calibrage auto' %}<br><i class="fa-solid fa-robot w3-text-orange w3-xxlarge"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scan w3-center">
|
||||
<button id="_goto-0" class="w3-button w3-dark-xlight w3-round w3-round-large w3-margin-small w3-block">
|
||||
Origine (0, 0)<br><i class="fa-solid fa-house w3-text-amber w3-xlarge"></i>
|
||||
</button>
|
||||
<button id="_xy-base" class="w3-button w3-warning w3-round-large w3-margin-small w3-block">
|
||||
<i class="fa-solid fa-circle-check"></i> {% trans 'Définir base' %}
|
||||
</button>
|
||||
<hr>
|
||||
<button id="_calib_debug" class="w3-button w3-teal w3-round-large w3-margin-small w3-block">
|
||||
<i class="fa-solid fa-triangle-exclamation"></i> {% trans 'Debug' %}
|
||||
<button id="_goto-xy" class="w3-button w3-dark-xlight w3-round-large w3-margin-small w3-block w3-margin-bottom">
|
||||
{% trans 'Aller à la base' %}<br>(<span id="_xbase"></span>, <span id="_ybase"></span>)<br><i class="fa-solid fa-bullseye w3-text-amber w3-xlarge"></i>
|
||||
</button>
|
||||
<button id="_median" class="w3-button w3-teal w3-round-large w3-margin-small w3-block">
|
||||
<i class="fa-solid fa-crosshairs"></i> {% trans 'Axes' %}
|
||||
</button>
|
||||
<button id="_crop" class="w3-button w3-teal w3-round-large w3-margin-small w3-block w3-margin-bottom">
|
||||
<i class="fa-solid fa-crop"></i> {% trans 'Recadrer' %}
|
||||
</button>
|
||||
<span>
|
||||
{% trans 'Rayon' %}: <input id="_crop_radius" type="number" min="100" max="1200" step="1" value="{{ conf.calibration_crop_radius }}" title="{% trans 'Rayon de cadrage' %}"/>
|
||||
</span>
|
||||
<hr>
|
||||
<button id="_test" class="w3-button w3-warning w3-round-large w3-margin-small w3-block">
|
||||
<i class="fa-solid fa-circle-check"></i> {% trans 'Tester le cirduit' %}
|
||||
<div class="w3-row">
|
||||
<div class="w3-half">
|
||||
<button id="_calib_debug" class="w3-button w3-dark-xlight w3-round-large w3-padding-small">
|
||||
{% trans 'Debug' %}<br><i class="fa-solid fa-triangle-exclamation w3-text-amber w3-xlarge"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w3-half">
|
||||
<button id="_median" class="w3-button w3-dark-xlight w3-round-large w3-padding-small">
|
||||
{% trans 'Axes' %} <br><i class="fa-solid fa-crosshairs w3-text-amber w3-xlarge"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w3-col">
|
||||
<button id="_crop" class="w3-button w3-dark-xlight w3-round-large w3-padding-small w3-margin-top w3-block">
|
||||
{% trans 'Recadrer' %}<br><i class="fa-solid fa-crop w3-text-amber w3-xlarge"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w3-col w3-padding-small w3-border w3-round-large w3-margin-top">
|
||||
<span class="w3-bold w3-text-amber">{% trans 'Rayon' %}</span><br>
|
||||
<input id="_crop_radius" type="number" min="100" max="1200" step="1" class="w3-round" title="{% trans 'Rayon du recadrement' %}"
|
||||
value="{{ conf.calibration_crop_radius }}" title="{% trans 'Rayon de cadrage' %}"> px
|
||||
</div>
|
||||
</div>
|
||||
<button id="_test" class="w3-button w3-dark-xlight w3-round-large w3-margin-small w3-block">
|
||||
{% trans 'Tester le cirduit' %}<br><i class="fa-solid fa-circle-check w3-text-orange w3-xxlarge"></i>
|
||||
</button>
|
||||
<button id="_halt" class="w3-button w3-red w3-round-large w3-margin-small w3-block">
|
||||
<i class="fa-solid fa-hand"></i> {% trans 'ARRET' %}
|
||||
{% trans 'ARRET' %}<br><i class="fa-solid fa-hand w3-xxlarge"></i>
|
||||
</button>
|
||||
</div>
|
||||
{% include 'scanner/scan-image.html' %}
|
||||
@@ -154,10 +185,49 @@
|
||||
const container = sId("scan-img");
|
||||
const ws_route = "{{ ws_route }}";
|
||||
const use_tracking = "{{ use_tracking }}" == "True";
|
||||
|
||||
const options = {
|
||||
ts : sId("_ts"),
|
||||
cx : sId("_cx"),
|
||||
cy : sId("_cy"),
|
||||
x : sId("_x"),
|
||||
y : sId("_y"),
|
||||
xbase: sId("_xbase"),
|
||||
ybase: sId("_ybase"),
|
||||
test : sId("_test"),
|
||||
halt : sId("_halt"),
|
||||
speed_px_s : sId("_speed_px_s"),
|
||||
axial_speed : sId("_axial_speed"),
|
||||
axial_pos : sId("_axial_pos"),
|
||||
area_px : sId("_area_px"),
|
||||
frame_count : sId("_count"),
|
||||
goto_0 : sId("_goto-0"),
|
||||
goto_xy : sId("_goto-xy"),
|
||||
xy_base : sId ("_xy-base"),
|
||||
up : sId("_up"),
|
||||
down : sId("_down"),
|
||||
left : sId("_left"),
|
||||
right : sId("_right"),
|
||||
duration: sId("_duration"),
|
||||
feed : sId("_feed"),
|
||||
step : sId("_step"),
|
||||
well : sId("_well"),
|
||||
debug : sId("_debug"),
|
||||
calib_debug : sId("_calib_debug"),
|
||||
calib_auto : sId("_calib_auto"),
|
||||
calib_center: sId("_calib_center"),
|
||||
previous : sId("_previous"),
|
||||
next : sId("_next"),
|
||||
set_well : sId("_set_well"),
|
||||
well_btn : sId("_well_btn"),
|
||||
median : sId("_median"),
|
||||
crop : sId("_crop"),
|
||||
crop_radius : sId("_crop_radius")
|
||||
};
|
||||
</script>
|
||||
<script src="/static/scanner/js/calibration.js"></script>
|
||||
<script>
|
||||
const manager = new ScannerManager(container);
|
||||
const manager = new ScannerManager(container, options);
|
||||
const protocol = location.protocol === "https:" ? "wss" : "ws";
|
||||
const wsUrl = `${protocol}://${location.host}/${ws_route}`;
|
||||
const socket = new MetadataSocket(wsUrl);
|
||||
|
||||
@@ -44,22 +44,37 @@
|
||||
{% block js_footer %}
|
||||
{{ block.super }}
|
||||
<script>
|
||||
const container = sId("scan-img");
|
||||
const ws_route = "{{ ws_route }}";
|
||||
const use_tracking = "{{ use_tracking }}" == "True";
|
||||
const container = sId("scan-img");
|
||||
const ws_route = "{{ ws_route }}";
|
||||
const use_tracking = "{{ use_tracking }}" == "True";
|
||||
const options = {
|
||||
ts: sId("_ts"),
|
||||
cx: sId("_cx"),
|
||||
cy: sId("_cy"),
|
||||
x : sId("_x"),
|
||||
y : sId("_y"),
|
||||
session: sId("_session"),
|
||||
scan : sId("_scan"),
|
||||
halt : sId("_halt"),
|
||||
debug : sId("_debug"),
|
||||
median : sId("_median"),
|
||||
crop : sId("_crop"),
|
||||
speed_px_s : sId("_speed_px_s"),
|
||||
axial_speed : sId("_axial_speed"),
|
||||
axial_pos : sId("_axial_pos"),
|
||||
area_px : sId("_area_px"),
|
||||
frame_count : sId("_count")
|
||||
};
|
||||
</script>
|
||||
<script src="/static/scanner/js/main.js"></script>
|
||||
<script>
|
||||
// ---- Point d'entrée ----
|
||||
(async () => {
|
||||
const manager = new ScannerManager(container);
|
||||
const protocol = location.protocol === "https:" ? "wss" : "ws";
|
||||
const wsUrl = `${protocol}://${location.host}/${ws_route}`;
|
||||
const socket = new MetadataSocket(wsUrl);
|
||||
socket.setManager(manager);
|
||||
socket.connect();
|
||||
manager.registerSocket(socket);
|
||||
})();
|
||||
const manager = new ScannerManager(container, options);
|
||||
const protocol = location.protocol === "https:" ? "wss" : "ws";
|
||||
const wsUrl = `${protocol}://${location.host}/${ws_route}`;
|
||||
const socket = new MetadataSocket(wsUrl);
|
||||
socket.setManager(manager);
|
||||
socket.connect();
|
||||
manager.registerSocket(socket);
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user