From f409de5b4b1ff04b1c824cb5922749e33fff78b2 Mon Sep 17 00:00:00 2001 From: Rodja Trappe Date: Thu, 29 Aug 2024 09:10:59 +0200 Subject: [PATCH] Allow recording weeding action in timelapse video (#155) * allow recording weeding action in timelapse video * fix name * fix name --------- Co-authored-by: Pascal Schade --- .../automations/implements/weeding_implement.py | 16 +++++++++++++--- field_friend/system.py | 4 ++++ main.py | 4 +++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/field_friend/automations/implements/weeding_implement.py b/field_friend/automations/implements/weeding_implement.py index 4e53cd47..734a03c4 100644 --- a/field_friend/automations/implements/weeding_implement.py +++ b/field_friend/automations/implements/weeding_implement.py @@ -31,6 +31,7 @@ def __init__(self, name: str, system: 'System', persistence_key: str = 'weeding self.system = system self.kpi_provider = system.kpi_provider self.puncher = system.puncher + self.record_video = False self.cultivated_crop: str | None = None self.crop_safety_distance: float = 0.01 @@ -63,17 +64,21 @@ async def prepare(self) -> bool: async def finish(self) -> None: self.system.plant_locator.pause() await self.system.field_friend.stop() + await self.system.timelapse_recorder.compress_video() await super().finish() async def activate(self): await self.system.field_friend.flashlight.turn_on() await self.puncher.clear_view() - self.system.plant_locator.resume() await rosys.sleep(3) + self.system.plant_locator.resume() + if self.record_video: + self.system.timelapse_recorder.camera = self.system.camera_provider.first_connected_camera await super().activate() async def deactivate(self): await super().deactivate() + self.system.timelapse_recorder.camera = None await self.system.field_friend.flashlight.turn_off() self.system.plant_locator.pause() self.kpi_provider.increment_weeding_kpi('rows_weeded') @@ -94,7 +99,7 @@ async def _check_hardware_ready(self) -> bool: rosys.notify('E-Stop is active, aborting', 'negative') self.log.error('E-Stop is active, aborting') return False - camera = next((camera for camera in self.system.camera_provider.cameras.values() if camera.is_connected), None) + camera = self.system.camera_provider.first_connected_camera if not camera: rosys.notify('no camera connected') return False @@ -166,7 +171,8 @@ 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 + 'crop_safety_distance': self.crop_safety_distance, + 'record_video': self.record_video, } def restore(self, data: dict[str, Any]) -> None: @@ -175,6 +181,7 @@ 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.record_video = data.get('record_video', self.record_video) def clear(self) -> None: self.crops_to_handle = {} @@ -209,3 +216,6 @@ 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('record video', on_change=self.request_backup) \ + .bind_value(self, 'record_video') \ + .tooltip('Set the weeding automation to record video') diff --git a/field_friend/system.py b/field_friend/system.py index a9deeef8..0875d133 100644 --- a/field_friend/system.py +++ b/field_friend/system.py @@ -123,6 +123,10 @@ def watch_robot() -> None: self.automator = rosys.automation.Automator(self.steerer, on_interrupt=self.field_friend.stop) self.automation_watcher = AutomationWatcher(self) self.monitoring = Recorder(self) + self.timelapse_recorder = rosys.analysis.TimelapseRecorder() + self.timelapse_recorder.frame_info_builder = lambda _: f'{self.version}, {self.current_navigation.name}, tags: {", ".join(self.plant_locator.tags)}' + rosys.NEW_NOTIFICATION.register(self.timelapse_recorder.notify) + rosys.on_startup(self.timelapse_recorder.compress_video) # NOTE: cleanup JPEGs from before last shutdown self.field_navigation = RowsOnFieldNavigation(self, self.monitoring) self.straight_line_navigation = StraightLineNavigation(self, self.monitoring) self.follow_crops_navigation = FollowCropsNavigation(self, self.monitoring) diff --git a/main.py b/main.py index 8b1c8b85..f290b37f 100755 --- a/main.py +++ b/main.py @@ -3,13 +3,14 @@ from dotenv import load_dotenv from nicegui import app, ui -from rosys.analysis import logging_page +from rosys.analysis import logging_page, videos_page import field_friend.log_configuration as log_configuration from field_friend import interface from field_friend.interface.components import header_bar, status_drawer, system_bar from field_friend.system import System + logger = log_configuration.configure() app.add_static_files('/assets', 'assets') @@ -46,6 +47,7 @@ def status(): return {'status': 'ok'} logging_page(['field_friend', 'rosys']) # /logging + videos_page() # /videos app.on_startup(startup)