diff --git a/scripts/planktoscope/imager.py b/scripts/planktoscope/imager.py index 6c144ba..85d53b0 100644 --- a/scripts/planktoscope/imager.py +++ b/scripts/planktoscope/imager.py @@ -12,10 +12,11 @@ import datetime import time # Libraries manipulate json format, execute bash commands -import json, shutil +import json # Library for path and filesystem manipulations import os +import shutil # Library for starting processes import multiprocessing @@ -166,6 +167,7 @@ class ImagerProcess(multiprocessing.Process): self.__pump_direction = "FORWARD" self.__img_goal = None self.imager_client = None + self.__error = 0 # Initialise the camera and the process # Also starts the streaming to the temporary file @@ -196,7 +198,7 @@ class ImagerProcess(multiprocessing.Process): self.__white_balance_gain = ( configuration.get("wb_red_gain", 2.00) * 100, configuration.get("wb_blue_gain", 1.40) * 100, - ) # Those values were tested on a HQ camera to give a whitish background + ) self.__base_path = "/home/pi/data/img" # Let's make sure the base path exists @@ -491,7 +493,9 @@ class ImagerProcess(multiprocessing.Process): self.__imager.change(planktoscope.imager_state_machine.Capture) self.imager_client.client.unsubscribe("status/pump") else: - logger.info(f"The pump is not done yet {payload}") + logger.info( + f"The pump is not done yet {self.imager_client.msg['payload']}" + ) else: logger.error( "There is an error, we received an unexpected pump message" @@ -641,39 +645,25 @@ class ImagerProcess(multiprocessing.Process): # Sleep a duration before to start acquisition time.sleep(self.__sleep_before) - # Capture an image with the proper filename + # Capture an image to the temporary file try: - self.__camera.capture(filename_path) + self.__camera.capture("", timeout=5) except TimeoutError as e: - logger.error("A timeout happened while waiting for a capture to happen") - # Publish the name of the image to via MQTT to Node-RED - self.imager_client.client.publish( - "status/imager", - f'{{"status":"Image {self.__img_done + 1}/{self.__img_goal} WAS NOT CAPTURED! STOPPING THE PROCESS!"}}', - ) - # Reset the counter to 0 - self.__img_done = 0 - self.__img_goal = 0 - self.__imager.change(planktoscope.imager_state_machine.Stop) - planktoscope.light.error() + self.__capture_error("timeout during capture") return + logger.debug(f"Copying the image from the temp file to {filename_path}") + shutil.copy("/dev/shm/mjpeg/image.jpg", filename_path) + logger.debug("Syncing the disk") + os.sync() + # Add the checksum of the captured image to the integrity file try: planktoscope.integrity.append_to_integrity_file(filename_path) except FileNotFoundError as e: - logger.error( - f"{filename_path} was not found, the camera did not work properly! Trying again" - ) - self.imager_client.client.publish( - "status/imager", - f'{{"status":"Image {self.__img_done + 1}/{self.__img_goal} was not found, retrying the capture now."}}', - ) - # Let's try again after a tiny delay! - time.sleep(1) + self.__capture_error(f"{filename_path} was not found") return - # Publish the name of the image to via MQTT to Node-RED self.imager_client.client.publish( "status/imager", f'{{"status":"Image {self.__img_done + 1}/{self.__img_goal} has been imaged to {filename}"}}', @@ -681,6 +671,7 @@ class ImagerProcess(multiprocessing.Process): # Increment the counter self.__img_done += 1 + self.__error = 0 # If counter reach the number of frame, break if self.__img_done >= self.__img_goal: @@ -690,7 +681,6 @@ class ImagerProcess(multiprocessing.Process): self.__imager.change(planktoscope.imager_state_machine.Stop) planktoscope.light.ready() - return else: # We have not reached the final stage, let's keep imaging self.imager_client.client.subscribe("status/pump") @@ -699,6 +689,27 @@ class ImagerProcess(multiprocessing.Process): self.__imager.change(planktoscope.imager_state_machine.Waiting) + def __capture_error(self, message=""): + logger.error(f"An error occurred during the capture: {message}") + planktoscope.light.error() + if self.__error: + logger.error("This is a repeating problem, stopping the capture now") + self.imager_client.client.publish( + "status/imager", + f'{{"status":"Image {self.__img_done + 1}/{self.__img_goal} WAS NOT CAPTURED! STOPPING THE PROCESS!"}}', + ) + self.__img_done = 0 + self.__img_goal = 0 + self.__error = 0 + self.__imager.change(planktoscope.imager_state_machine.Stop) + else: + self.__error += 1 + self.imager_client.client.publish( + "status/imager", + f'{{"status":"Image {self.__img_done + 1}/{self.__img_goal} was not captured due to this error:{message}! Retrying once!"}}', + ) + time.sleep(1) + @logger.catch def state_machine(self): if self.__imager.state.name == "imaging": diff --git a/scripts/planktoscope/raspimjpeg.py b/scripts/planktoscope/raspimjpeg.py index 0ac8586..f74be3c 100644 --- a/scripts/planktoscope/raspimjpeg.py +++ b/scripts/planktoscope/raspimjpeg.py @@ -458,9 +458,10 @@ class raspimjpeg(object): self.__send_command(f"im") else: self.__send_command(f"im {path}") + time.sleep(0.1) self.__wait_for_output("Capturing image", timeout / 2) - self.__wait_for_status("ready", timeout / 2) + self.__wait_for_output("Ready", timeout / 2) def stop(self): """Halt and release the camera. """ diff --git a/scripts/planktoscope/stepper.py b/scripts/planktoscope/stepper.py index e0d84ed..c4acad6 100644 --- a/scripts/planktoscope/stepper.py +++ b/scripts/planktoscope/stepper.py @@ -354,7 +354,7 @@ class StepperProcess(multiprocessing.Process): command = self.actuator_client.msg["topic"].split("/", 1)[1] logger.debug(command) self.actuator_client.read_message() - + if command == "pump": self.__message_pump(last_message) elif command == "focus": diff --git a/scripts/raspimjpeg/bin/raspimjpeg b/scripts/raspimjpeg/bin/raspimjpeg index 9c197a9..665222c 100755 Binary files a/scripts/raspimjpeg/bin/raspimjpeg and b/scripts/raspimjpeg/bin/raspimjpeg differ diff --git a/scripts/raspimjpeg/raspimjpeg.conf b/scripts/raspimjpeg/raspimjpeg.conf index 877c68a..6e4ef50 100644 --- a/scripts/raspimjpeg/raspimjpeg.conf +++ b/scripts/raspimjpeg/raspimjpeg.conf @@ -120,9 +120,9 @@ motion_file 0 # image, video and lapse may be configured relative to media_path if first / left out base_path /home/pi/PlanktonScope/scripts/raspimjpeg/ preview_path /dev/shm/mjpeg/cam.jpg -image_path /home/pi/data/%Y%M%D/im_%i_%h%m%s.jpg -lapse_path /home/pi/media/tl_%i_%t_%Y%M%D_%h%m%s.jpg -video_path /home/pi/media/vi_%v_%Y%M%D_%h%m%s.mp4 +image_path /dev/shm/mjpeg/image.jpg +lapse_path /dev/shm/mjpeg/tl_%i_%t_%Y%M%D_%h%m%s.jpg +video_path /dev/shm/mjpeg/vi_%v_%Y%M%D_%h%m%s.mp4 status_file /dev/shm/mjpeg/status_mjpeg.txt control_file /dev/shm/mjpeg/FIFO media_path /home/pi/data/ @@ -165,7 +165,7 @@ watchdog_errors 5 # Set callback_timeout to 0 to disable it callback_timeout 30 #optional user_config file to overwrite (persist) changes -user_config /home/pi/PlanktonScope/scripts/raspimjpeg/uconfig +user_config #logfile for raspimjpeg, default to merge with scheduler log log_file /home/pi/PlanktonScope/scripts/raspimjpeg/raspimjpeg.log