Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
45 changes: 7 additions & 38 deletions device-backend/control/adafruithat/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,37 +23,8 @@

from loguru import logger # for logging with multiprocessing

import planktoscope.mqtt
import planktoscope.stepper
import planktoscope.light # Fan HAT LEDs
import planktoscope.identity
import planktoscope.display # Fan HAT OLED screen
from planktoscope.imager import mqtt as imager

# enqueue=True is necessary so we can log across modules
# rotation happens everyday at 01:00 if not restarted
logs_path = "/home/pi/device-backend-logs/control"
if not os.path.exists(logs_path):
os.makedirs(logs_path)
logger.add(
# sys.stdout,
"/home/pi/device-backend-logs/control/{time}.log",
rotation="5 MB",
retention="1 week",
compression=".tar.gz",
enqueue=True,
level="DEBUG",
)

# The available level for the logger are as follows:
# Level name Severity Logger method
# TRACE 5 logger.trace()
# DEBUG 10 logger.debug()
# INFO 20 logger.info()
# SUCCESS 25 logger.success()
# WARNING 30 logger.warning()
# ERROR 40 logger.error()
# CRITICAL 50 logger.critical()
from adafruithat.planktoscope import stepper, light, identity, display
from adafruithat.planktoscope.imager import mqtt as imager

logger.info("Starting the PlanktoScope hardware controller!")

Expand All @@ -67,7 +38,7 @@ def handler_stop_signals(signum, _):
run = False


if __name__ == "__main__":
def main():
logger.info("Welcome!")
logger.info(
"Initialising signals handling and sanitizing the directories (step 1/4)"
Expand Down Expand Up @@ -97,17 +68,15 @@ def handler_stop_signals(signum, _):
# create the path!
os.makedirs(img_path)

logger.info(
f"This PlanktoScope's machine name is {planktoscope.identity.load_machine_name()}"
)
logger.info(f"This PlanktoScope's machine name is {identity.load_machine_name()}")

# Prepare the event for a graceful shutdown
shutdown_event = multiprocessing.Event()
shutdown_event.clear()

# Starts the stepper process for actuators
logger.info("Starting the stepper control process (step 2/4)")
stepper_thread = planktoscope.stepper.StepperProcess(shutdown_event)
stepper_thread = stepper.StepperProcess(shutdown_event)
stepper_thread.start()

# Starts the imager control process
Expand All @@ -121,10 +90,10 @@ def handler_stop_signals(signum, _):
imager_thread.start()

logger.info("Starting the display module (step 4/4)")
display = planktoscope.display.Display()
display = display.Display()

logger.success("Looks like the controller is set up and running, have fun!")
planktoscope.light.ready()
light.ready()

while run:
# TODO look into ways of restarting the dead threads
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

import loguru

from planktoscope import mqtt as messaging
from planktoscope.camera import hardware, mjpeg
from .. import mqtt as messaging
from . import hardware, mjpeg

loguru.logger.info("planktoscope.camera is loaded")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import picamera2 # type: ignore
from picamera2 import encoders, outputs

from planktoscope.camera import hardware, mjpeg
from . import hardware, mjpeg


def main() -> None:
Expand Down
9 changes: 3 additions & 6 deletions device-backend/control/adafruithat/planktoscope/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,15 @@
# Logger library compatible with multiprocessing
from loguru import logger

import datetime
import os

import Adafruit_SSD1306

import PIL.Image
import PIL.ImageDraw
import PIL.ImageFont

logger.info("planktoscope.display is loading")
from . import identity

import planktoscope.identity
logger.info("planktoscope.display is loading")


class Display(object):
Expand All @@ -54,7 +51,7 @@ def __init__(self):
def display_machine_name(self):
if self.display_available:
self.__clear()
machineName = planktoscope.identity.load_machine_name()
machineName = identity.load_machine_name()
self.display_text(machineName.replace(" ", "\n"))

def display_text(self, message):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@

import loguru

from planktoscope import identity, integrity, mqtt
from planktoscope.camera import mqtt as camera
from planktoscope.imager import stopflow
from .. import identity, integrity, mqtt
from ..camera import mqtt as camera
from . import stopflow

loguru.logger.info("planktoscope.imager is loaded")

Expand Down
18 changes: 9 additions & 9 deletions device-backend/control/adafruithat/planktoscope/stepper.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
import time
import json
import os
import planktoscope.mqtt
import planktoscope.light
import multiprocessing
import RPi.GPIO

from . import mqtt, light

# Logger library compatible with multiprocessing
from loguru import logger

Expand Down Expand Up @@ -203,11 +203,11 @@ def __message_pump(self, last_message):
"status/pump", '{"status":"Interrupted"}'
)

planktoscope.light.ready()
light.ready()

elif last_message["action"] == "move":
logger.debug("We have received a move pump command")
planktoscope.light.pumping()
light.pumping()

if (
"direction" not in last_message
Expand Down Expand Up @@ -256,11 +256,11 @@ def __message_focus(self, last_message):
"status/focus", '{"status":"Interrupted"}'
)

planktoscope.light.ready()
light.ready()

elif last_message["action"] == "move":
logger.debug("We have received a move focus command")
planktoscope.light.focusing()
light.focusing()

if "direction" not in last_message or "distance" not in last_message:
logger.error(
Expand Down Expand Up @@ -422,7 +422,7 @@ def run(self):
# it doesn't see changes and calls made by self.actuator_client because this one
# only exist in the master process
# see https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class
self.actuator_client = planktoscope.mqtt.MQTT_Client(
self.actuator_client = mqtt.MQTT_Client(
topic="actuator/#", name="actuator_client"
)
# Publish the status "Ready" to via MQTT to Node-RED
Expand All @@ -437,14 +437,14 @@ def run(self):
self.treat_command()
if self.pump_stepper.move():
delay = 0.0001
planktoscope.light.ready()
light.ready()
self.actuator_client.client.publish(
"status/pump",
'{"status":"Done"}',
)
if self.focus_stepper.move():
delay = 0.0001
planktoscope.light.ready()
light.ready()
self.actuator_client.client.publish(
"status/focus",
'{"status":"Done"}',
Expand Down
80 changes: 80 additions & 0 deletions device-backend/control/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import json
from os import makedirs, path

from loguru import logger

# enqueue=True is necessary so we can log across modules
# rotation happens everyday at 01:00 if not restarted
logs_path = "/home/pi/device-backend-logs/control"
if not path.exists(logs_path):
makedirs(logs_path)
logger.add(
# sys.stdout,
"/home/pi/device-backend-logs/control/{time}.log",
rotation="5 MB",
retention="1 week",
compression=".tar.gz",
enqueue=True,
level="DEBUG",
)

# The available level for the logger are as follows:
# Level name Severity Logger method
# TRACE 5 logger.trace()
# DEBUG 10 logger.debug()
# INFO 20 logger.info()
# SUCCESS 25 logger.success()
# WARNING 30 logger.warning()
# ERROR 40 logger.error()
# CRITICAL 50 logger.critical()

# This is a special case for legacy hardware; new hardware designs should all be part of the
# planktoscopehat codebase:
CONFIG_PATH = "/home/pi/PlanktoScope/config.json"


def load_variant_setting(config_path: str = CONFIG_PATH):
config = {}
try:
with open(config_path, "r") as file:
try:
config = json.load(file)
except Exception:
logger.exception(f"Couldn't parse {config_path} as JSON file")
return None
except Exception:
logger.exception(f"Couldn't open {config_path}")
return None

if "acq_instrument" not in config:
logger.error(f"{config_path} lacks a 'acq_instrument' field")
return None

if config["acq_instrument"] == "PlanktoScope v2.1":
return "adafruithat"
return "planktoscopehat"


def main():
logger.info("Determining configured hardware variant...")
variant = load_variant_setting()
if variant is None:
variant = "planktoscopehat"
logger.warning(
f"Couldn't load hardware variant setting from config, defaulting to {variant}"
)
logger.info(f"Hardware variant: {variant}")

if variant == "adafruithat":
from adafruithat import main as platform # noqa: IMR200
else:
from planktoscopehat import main as platform # noqa: IMR200

platform.main()


if __name__ == "__main__":
try:
main()
except Exception:
logger.exception("Unhandled exception")
Empty file.
45 changes: 7 additions & 38 deletions device-backend/control/planktoscopehat/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,8 @@

from loguru import logger

import planktoscope.mqtt
import planktoscope.pump
import planktoscope.focus
import planktoscope.light
import planktoscope.identity
from planktoscope.imager import mqtt as imager

# enqueue=True is necessary so we can log across modules
# rotation happens everyday at 01:00 if not restarted
logs_path = "/home/pi/device-backend-logs/control"
if not os.path.exists(logs_path):
os.makedirs(logs_path)
logger.add(
# sys.stdout,
"/home/pi/device-backend-logs/control/{time}.log",
rotation="5 MB",
retention="1 week",
compression=".tar.gz",
enqueue=True,
level="DEBUG",
)

# The available level for the logger are as follows:
# Level name Severity Logger method
# TRACE 5 logger.trace()
# DEBUG 10 logger.debug()
# INFO 20 logger.info()
# SUCCESS 25 logger.success()
# WARNING 30 logger.warning()
# ERROR 40 logger.error()
# CRITICAL 50 logger.critical()
from planktoscopehat.planktoscope import pump, focus, light, identity
from planktoscopehat.planktoscope.imager import mqtt as imager

logger.info("Starting the PlanktoScope python script!")

Expand All @@ -51,7 +22,7 @@ def handler_stop_signals(signum, frame):
run = False


if __name__ == "__main__":
def main():
logger.info("Welcome!")
logger.info(
"Initialising configuration, signals handling and sanitizing the directories (step 1/5)"
Expand Down Expand Up @@ -81,9 +52,7 @@ def handler_stop_signals(signum, frame):
# create the path!
os.makedirs(img_path)

logger.info(
f"This PlanktoScope's machine name is {planktoscope.identity.load_machine_name()}"
)
logger.info(f"This PlanktoScope's machine name is {identity.load_machine_name()}")

try:
with open("/home/pi/PlanktoScope/hardware.json", "r") as config_file:
Expand All @@ -100,12 +69,12 @@ def handler_stop_signals(signum, frame):

# Starts the pump process
logger.info("Starting the pump control process (step 2/5)")
pump_thread = planktoscope.pump.PumpProcess(shutdown_event, configuration)
pump_thread = pump.PumpProcess(shutdown_event, configuration)
pump_thread.start()

# Starts the focus process
logger.info("Starting the focus control process (step 3/5)")
focus_thread = planktoscope.focus.FocusProcess(shutdown_event, configuration)
focus_thread = focus.FocusProcess(shutdown_event, configuration)
focus_thread.start()

# TODO try to isolate the imager thread (or another thread)
Expand All @@ -122,7 +91,7 @@ def handler_stop_signals(signum, frame):
# Starts the light process
logger.info("Starting the light control process (step 5/5)")
try:
light_thread = planktoscope.light.LightProcess(shutdown_event, configuration)
light_thread = light.LightProcess(shutdown_event, configuration)
except Exception:
logger.error("The light control process could not be started")
light_thread = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

import loguru

from planktoscope import mqtt as messaging
from planktoscope.camera import hardware, mjpeg
from .. import mqtt as messaging
from . import hardware, mjpeg

loguru.logger.info("planktoscope.camera is loaded")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import picamera2 # type: ignore
from picamera2 import encoders, outputs

from planktoscope.camera import hardware, mjpeg
from . import hardware, mjpeg


def main() -> None:
Expand Down
Loading