Skip to content

Commit 6a93161

Browse files
authored
backend: Read hardware config once (#600)
Fixes #578
1 parent 20a9e0a commit 6a93161

File tree

6 files changed

+31
-60
lines changed

6 files changed

+31
-60
lines changed

device-backend/control/planktoscopehat/main.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import time
44
import signal
55
import os
6+
import json
67

78
from loguru import logger
89

@@ -53,7 +54,7 @@ def handler_stop_signals(signum, frame):
5354
if __name__ == "__main__":
5455
logger.info("Welcome!")
5556
logger.info(
56-
"Initialising signals handling and sanitizing the directories (step 1/5)"
57+
"Initialising configuration, signals handling and sanitizing the directories (step 1/5)"
5758
)
5859
signal.signal(signal.SIGINT, handler_stop_signals)
5960
signal.signal(signal.SIGTERM, handler_stop_signals)
@@ -84,25 +85,34 @@ def handler_stop_signals(signum, frame):
8485
f"This PlanktoScope's machine name is {planktoscope.identity.load_machine_name()}"
8586
)
8687

88+
try:
89+
with open("/home/pi/PlanktoScope/hardware.json", "r") as config_file:
90+
configuration = json.load(config_file)
91+
except FileNotFoundError:
92+
logger.info(
93+
"The hardware configuration file doesn't exists, using defaults"
94+
)
95+
configuration = {}
96+
8797
# Prepare the event for a graceful shutdown
8898
shutdown_event = multiprocessing.Event()
8999
shutdown_event.clear()
90100

91101
# Starts the pump process
92102
logger.info("Starting the pump control process (step 2/5)")
93-
pump_thread = planktoscope.pump.PumpProcess(shutdown_event)
103+
pump_thread = planktoscope.pump.PumpProcess(shutdown_event, configuration)
94104
pump_thread.start()
95105

96106
# Starts the focus process
97107
logger.info("Starting the focus control process (step 3/5)")
98-
focus_thread = planktoscope.focus.FocusProcess(shutdown_event)
108+
focus_thread = planktoscope.focus.FocusProcess(shutdown_event, configuration)
99109
focus_thread.start()
100110

101111
# TODO try to isolate the imager thread (or another thread)
102112
# Starts the imager control process
103113
logger.info("Starting the imager control process (step 4/5)")
104114
try:
105-
imager_thread = imager.Worker(shutdown_event)
115+
imager_thread = imager.Worker(shutdown_event, configuration)
106116
except Exception as e:
107117
logger.error(f"The imager control process could not be started: {e}")
108118
imager_thread = None
@@ -112,7 +122,7 @@ def handler_stop_signals(signum, frame):
112122
# Starts the light process
113123
logger.info("Starting the light control process (step 5/5)")
114124
try:
115-
light_thread = planktoscope.light.LightProcess(shutdown_event)
125+
light_thread = planktoscope.light.LightProcess(shutdown_event, configuration)
116126
except Exception:
117127
logger.error("The light control process could not be started")
118128
light_thread = None

device-backend/control/planktoscopehat/planktoscope/camera/mqtt.py

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""mqtt provides an MJPEG+MQTT API for camera supervision and interaction."""
22

33
import json
4-
import os
54
import threading
65
import time
76
import typing
@@ -17,7 +16,11 @@
1716
class Worker(threading.Thread):
1817
"""Runs a camera with live MJPEG preview and an MQTT API for adjusting camera settings."""
1918

20-
def __init__(self, mjpeg_server_address: tuple[str, int] = ("", 8000)) -> None:
19+
def __init__(
20+
self,
21+
configuration: dict[str, typing.Any],
22+
mjpeg_server_address: tuple[str, int] = ("", 8000),
23+
) -> None:
2124
"""Initialize the backend.
2225
2326
Args:
@@ -50,19 +53,7 @@ def __init__(self, mjpeg_server_address: tuple[str, int] = ("", 8000)) -> None:
5053
sharpness=0, # disable the default "normal" sharpening level
5154
jpeg_quality=95, # maximize image quality
5255
)
53-
if os.path.exists("/home/pi/PlanktoScope/hardware.json"):
54-
# load hardware.json
55-
with open("/home/pi/PlanktoScope/hardware.json", "r", encoding="utf-8") as config_file:
56-
hardware_config = json.load(config_file)
57-
loguru.logger.debug(
58-
f"Loaded hardware configuration file: {hardware_config}",
59-
)
60-
settings = settings.overlay(hardware.config_to_settings_values(hardware_config))
61-
else:
62-
loguru.logger.info(
63-
"The hardware configuration file doesn't exist, using default settings: "
64-
+ f"{settings}"
65-
)
56+
settings = settings.overlay(hardware.config_to_settings_values(configuration))
6657

6758
# I/O
6859
self._preview_stream: hardware.PreviewStream = hardware.PreviewStream()

device-backend/control/planktoscopehat/planktoscope/focus.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import json
21
import multiprocessing
32
import os
43
import time
@@ -22,23 +21,13 @@ class FocusProcess(multiprocessing.Process):
2221
# focus max speed is in mm/sec and is limited by the maximum number of pulses per second the PlanktoScope can send
2322
focus_max_speed = 5
2423

25-
def __init__(self, event):
24+
def __init__(self, event, configuration):
2625
super(FocusProcess, self).__init__()
2726
logger.info("Initialising the focus process")
2827

2928
self.stop_event = event
3029
self.focus_started = False
3130

32-
if os.path.exists("/home/pi/PlanktoScope/hardware.json"):
33-
# load hardware.json
34-
with open("/home/pi/PlanktoScope/hardware.json", "r") as config_file:
35-
# TODO #100 insert guard for config_file empty
36-
configuration = json.load(config_file)
37-
logger.debug(f"Hardware configuration loaded is {configuration}")
38-
else:
39-
logger.info("The hardware configuration file doesn't exists, using defaults")
40-
configuration = {}
41-
4231
# parse the config data. If the key is absent, we are using the default value
4332
self.focus_steps_per_mm = configuration.get("focus_steps_per_mm", self.focus_steps_per_mm)
4433
self.focus_max_speed = configuration.get("focus_max_speed", self.focus_max_speed)

device-backend/control/planktoscopehat/planktoscope/imager/mqtt.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class Worker(multiprocessing.Process):
2929
# TODO(ethanjli): instead of passing in a stop_event, just expose a `close()` method! This
3030
# way, we don't give any process the ability to stop all other processes watching the same
3131
# stop_event!
32-
def __init__(self, stop_event: threading.Event) -> None:
32+
def __init__(self, stop_event: threading.Event, configuration) -> None:
3333
"""Initialize the worker's internal state, but don't start anything yet.
3434
3535
Args:
@@ -52,6 +52,8 @@ def __init__(self, stop_event: threading.Event) -> None:
5252
# constructor.
5353
self._camera: typing.Optional[camera.Worker] = None
5454

55+
self.configuration = configuration
56+
5557
loguru.logger.success("planktoscope.imager is initialized and ready to go!")
5658

5759
@loguru.logger.catch
@@ -74,7 +76,7 @@ def run(self) -> None:
7476
loguru.logger.success("Pump RPC client is ready!")
7577

7678
loguru.logger.info("Starting the camera...")
77-
self._camera = camera.Worker()
79+
self._camera = camera.Worker(self.configuration)
7880
self._camera.start()
7981
if self._camera.camera is None:
8082
loguru.logger.error(

device-backend/control/planktoscopehat/planktoscope/light.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
################################################################################
44
# Logger library compatible with multiprocessing
55
from loguru import logger
6-
import json
76

87
from gpiozero import DigitalOutputDevice
98

@@ -40,22 +39,13 @@ class Register(enum.IntEnum):
4039
# This constant defines the current (mA) sent to the LED, 10 allows the use of the full ISO scale and results in a voltage of 2.77v
4140
DEFAULT_CURRENT = 10
4241

43-
def __init__(self):
44-
try:
45-
with open("/home/pi/PlanktoScope/hardware.json", "r") as config_file:
46-
configuration = json.load(config_file)
47-
except FileNotFoundError:
48-
logger.info(
49-
"The hardware configuration file doesn't exists, using defaults"
50-
)
51-
configuration = {}
52-
42+
def __init__(self, configuration):
5343
hat_type = configuration.get("hat_type") or ""
5444
hat_version = float(configuration.get("hat_version") or 0)
5545

5646
# The led is controlled by LM36011
5747
# but on version 1.2 of the PlanktoScope HAT (PlanktoScope v2.6)
58-
# the circuit is connected to that pin so it needs to be high
48+
# the circuit is connected to the pin 18 so it needs to be high
5949
# pin is assigned to self to prevent gpiozero from immediately releasing it
6050
if hat_type != "planktoscope" or hat_version < 3.1:
6151
self.__pin = DigitalOutputDevice(pin=18, initial_value=True)
@@ -176,7 +166,7 @@ def _read_byte(self, address):
176166
class LightProcess(multiprocessing.Process):
177167
"""This class contains the main definitions for the light of the PlanktoScope"""
178168

179-
def __init__(self, event):
169+
def __init__(self, event, configuration):
180170
"""Initialize the Light class
181171
182172
Args:
@@ -189,7 +179,7 @@ def __init__(self, event):
189179
self.stop_event = event
190180
self.light_client = None
191181
try:
192-
self.led = i2c_led()
182+
self.led = i2c_led(configuration)
193183
self.led.set_torch_current(self.led.DEFAULT_CURRENT)
194184
self.led.activate_torch_ramp()
195185
self.led.activate_torch()

device-backend/control/planktoscopehat/planktoscope/pump.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import json
21
import multiprocessing
32
import os
43
import time
@@ -24,23 +23,13 @@ class PumpProcess(multiprocessing.Process):
2423
# pump max speed is in ml/min
2524
pump_max_speed = 50
2625

27-
def __init__(self, event):
26+
def __init__(self, event, configuration):
2827
super(PumpProcess, self).__init__()
2928
logger.info("Initialising the pump process")
3029

3130
self.stop_event = event
3231
self.pump_started = False
3332

34-
if os.path.exists("/home/pi/PlanktoScope/hardware.json"):
35-
# load hardware.json
36-
with open("/home/pi/PlanktoScope/hardware.json", "r") as config_file:
37-
# TODO #100 insert guard for config_file empty
38-
configuration = json.load(config_file)
39-
logger.debug(f"Hardware configuration loaded is {configuration}")
40-
else:
41-
logger.info("The hardware configuration file doesn't exists, using defaults")
42-
configuration = {}
43-
4433
# parse the config data. If the key is absent, we are using the default value
4534
self.pump_steps_per_ml = configuration.get("pump_steps_per_ml", self.pump_steps_per_ml)
4635
self.pump_max_speed = configuration.get("pump_max_speed", self.pump_max_speed)

0 commit comments

Comments
 (0)