diff --git a/field_friend/automations/implements/punch_dialog.py b/field_friend/automations/implements/punch_dialog.py
deleted file mode 100644
index f96c95d0..00000000
--- a/field_friend/automations/implements/punch_dialog.py
+++ /dev/null
@@ -1,137 +0,0 @@
-from typing import TYPE_CHECKING, Optional
-
-import rosys
-from nicegui import ui
-from rosys.automation import Automator, automation_controls
-from rosys.driving import Odometer
-from rosys.geometry import Point3d
-from rosys.vision import Image
-
-from ..plant import Plant
-
-if TYPE_CHECKING:
- from field_friend.system import System
-
- from ...automations import PlantLocator
-
-
-class PunchDialog(ui.dialog):
- def __init__(self, system: 'System', shrink_factor: int = 1, timeout: float = 20.0, ui_update_rate: float = 0.2) -> None:
- super().__init__()
- self.camera_provider = system.camera_provider
- self.plant_locator: 'PlantLocator' = system.plant_locator
- self.odometer: Odometer = system.odometer
- self.automator: Automator = system.automator
- self.shrink_factor: int = shrink_factor
- self.timeout: float = timeout
- self._duration_left: float = timeout
- self.ui_update_rate: float = ui_update_rate
-
- self.camera: Optional[rosys.vision.CalibratableCamera] = None
- self.static_image_view: Optional[ui.interactive_image] = None
- self.live_image_view: Optional[ui.interactive_image] = None
- self.target_plant: Optional[Plant] = None
- self.timer = ui.timer(self.ui_update_rate, self.update_live_view)
- self.setup_camera()
- with self, ui.card().style('max-width: 1400px'):
- with ui.row(wrap=False):
- with ui.column().classes('w-1/2'):
- ui.label('Last detection').classes('text-lg')
- self.static_image_view = ui.interactive_image('')
- with ui.column().classes('w-1/2'):
- ui.label('Live').classes('text-lg')
- self.live_image_view = ui.interactive_image('')
- self.label = ui.label('Do you want to punch at the current position?').classes('text-lg')
- with ui.row().classes('w-full'):
- # TODO: doesn't load properly on first open
- ui.circular_progress(min=0.0, max=self.timeout,
- show_value=False, size='lg').bind_value_from(self, '_duration_left')
- ui.button('Yes', on_click=lambda: self.submit('Yes'))
- ui.button('No', on_click=lambda: self.submit('No'))
- ui.space()
- automation_controls(self.automator)
-
- def open(self) -> None:
- self._duration_left = self.timeout
- assert self.target_plant is not None
- assert self.camera is not None
- detection_image = self.camera.latest_detected_image if self.target_plant.detection_image is None else self.target_plant.detection_image
- assert detection_image is not None
- assert self.static_image_view is not None
- self.update_content(self.static_image_view, detection_image, draw_target=True)
- self.timer.activate()
- super().open()
-
- def close(self) -> None:
- self.timer.deactivate()
- super().close()
-
- def setup_camera(self) -> None:
- cameras = list(self.camera_provider.cameras.values())
- active_camera = next((camera for camera in cameras if camera.is_connected), None)
- assert active_camera is not None
- assert isinstance(active_camera, rosys.vision.CalibratableCamera)
- self.camera = active_camera
-
- def update_content(self, image_view: ui.interactive_image, image: Image, draw_target: bool = False) -> None:
- self._duration_left -= self.ui_update_rate
- assert self.camera is not None
- if self.shrink_factor > 1:
- url = f'{self.camera.get_latest_image_url()}?shrink={self.shrink_factor}'
- else:
- url = self.camera.get_latest_image_url()
- image_view.set_source(url)
- if image and image.detections:
- target_point = None
- confidence = None
- assert self.camera.calibration is not None
- if self.target_plant and draw_target:
- confidence = self.target_plant.confidence
- point = self.target_plant.position
- target_point = self.camera.calibration.project_to_image(Point3d(x=point.x, y=point.y, z=0))
- image_view.set_content(self.to_svg(image.detections, target_point, confidence))
-
- def update_live_view(self) -> None:
- assert self.live_image_view
- if self.camera is None:
- self.live_image_view.set_source('assets/field_friend.webp')
- return
- if self.camera.latest_detected_image is not None:
- self.update_content(self.live_image_view, self.camera.latest_detected_image)
-
- def to_svg(self, detections: rosys.vision.Detections, target_point: Optional[rosys.geometry.Point], confidence: Optional[float], color: str = 'blue') -> str:
- svg = ''
- cross_size = 20
- for point in detections.points:
- x = point.x / self.shrink_factor
- y = point.y / self.shrink_factor
- if point.category_name in self.plant_locator.crop_category_names:
- if point.confidence > self.plant_locator.minimum_crop_confidence:
- svg += f''''''
- svg += f'''Crop'''
- else:
- svg += f''''''
- svg += f'''{point.category_name}'''
- elif point.category_name in self.plant_locator.weed_category_names:
- if point.confidence > self.plant_locator.minimum_weed_confidence:
- svg += f'''
-
-
- Weed
- '''
- else:
- svg += f'''
-
-
- Weed
- '''
- if target_point and confidence:
- x = target_point.x / self.shrink_factor
- y = target_point.y / self.shrink_factor
- svg += f''
- svg += f'{confidence}'
- return svg
diff --git a/field_friend/automations/implements/tornado.py b/field_friend/automations/implements/tornado.py
index 82f6e501..bae09a0f 100644
--- a/field_friend/automations/implements/tornado.py
+++ b/field_friend/automations/implements/tornado.py
@@ -79,7 +79,7 @@ async def get_stretch(self, max_distance: float) -> float:
return self.WORKING_DISTANCE
if stretch < 0:
stretch = 0
- if stretch < max_distance and await self.ask_for_punch(closest_crop_id):
+ if stretch < max_distance:
self.next_punch_y_position = closest_crop_position.y
return stretch
return self.WORKING_DISTANCE
diff --git a/field_friend/automations/implements/weeding_implement.py b/field_friend/automations/implements/weeding_implement.py
index ad5f2018..4e53cd47 100644
--- a/field_friend/automations/implements/weeding_implement.py
+++ b/field_friend/automations/implements/weeding_implement.py
@@ -9,7 +9,6 @@
from ...hardware import ChainAxis
from .implement import Implement
-from .punch_dialog import PunchDialog
if TYPE_CHECKING:
from system import System
@@ -34,7 +33,6 @@ def __init__(self, name: str, system: 'System', persistence_key: str = 'weeding
self.puncher = system.puncher
self.cultivated_crop: str | None = None
self.crop_safety_distance: float = 0.01
- self.with_punch_check: bool = False
# dual mechanism
self.with_drilling: bool = False
@@ -50,7 +48,6 @@ def __init__(self, name: str, system: 'System', persistence_key: str = 'weeding
self.last_punches: deque[rosys.geometry.Point] = deque(maxlen=5)
self.next_punch_y_position: float = 0
- self.punch_dialog: PunchDialog | None = None
rosys.on_repeat(self._update_time_and_distance, 0.1)
async def prepare(self) -> bool:
@@ -169,8 +166,7 @@ def backup(self) -> dict:
'with_chopping': self.with_chopping,
'chop_if_no_crops': self.chop_if_no_crops,
'cultivated_crop': self.cultivated_crop,
- 'crop_safety_distance': self.crop_safety_distance,
- 'with_punch_check': self.with_punch_check,
+ 'crop_safety_distance': self.crop_safety_distance
}
def restore(self, data: dict[str, Any]) -> None:
@@ -179,7 +175,6 @@ def restore(self, data: dict[str, Any]) -> None:
self.chop_if_no_crops = data.get('chop_if_no_crops', self.chop_if_no_crops)
self.cultivated_crop = data.get('cultivated_crop', self.cultivated_crop)
self.crop_safety_distance = data.get('crop_safety_distance', self.crop_safety_distance)
- self.with_punch_check = data.get('with_punch_check', self.with_punch_check)
def clear(self) -> None:
self.crops_to_handle = {}
@@ -214,22 +209,3 @@ def settings_ui(self):
.classes('w-24') \
.bind_value(self, 'crop_safety_distance') \
.tooltip('Set the crop safety distance for the weeding automation')
- ui.checkbox('With punch check', value=True) \
- .bind_value(self, 'with_punch_check') \
- .tooltip('Set the weeding automation to check for punch')
- self.punch_dialog = PunchDialog(self.system)
-
- async def ask_for_punch(self, plant_id: str | None = None) -> bool:
- if not self.with_punch_check or plant_id is None or self.punch_dialog is None:
- return True
- self.punch_dialog.target_plant = self.system.plant_provider.get_plant_by_id(plant_id)
- result: str | None = None
- try:
- result = await asyncio.wait_for(self.punch_dialog, timeout=self.punch_dialog.timeout)
- if result == 'Yes':
- self.log.info('punching was allowed')
- return True
- except asyncio.TimeoutError:
- self.punch_dialog.close()
- self.log.warning('punch was not allowed')
- return False
diff --git a/field_friend/automations/implements/weeding_screw.py b/field_friend/automations/implements/weeding_screw.py
index 575c6587..7865a9e7 100644
--- a/field_friend/automations/implements/weeding_screw.py
+++ b/field_friend/automations/implements/weeding_screw.py
@@ -68,7 +68,7 @@ async def get_stretch(self, max_distance: float) -> float:
stretch = 0
self.log.info(f'Targeting weed {next_weed_id} which is {stretch} away at world: '
f'{weed_world_position}, local: {next_weed_position}')
- if stretch < max_distance and await self.ask_for_punch(next_weed_id):
+ if stretch < max_distance:
self.next_punch_y_position = next_weed_position.y
return stretch
else: