From be94df7c8ab93edeefb37560de3ccb7500a3b368 Mon Sep 17 00:00:00 2001
From: Julian Stirling
Date: Sun, 13 Jul 2025 11:14:20 +0100
Subject: [PATCH] Merge SettingsManager and SystemControl Things.
---
ofm_config_full.json | 3 +-
ofm_config_simulation.json | 3 +-
.../things/{settings_manager.py => system.py} | 58 ++++++++++++++++---
.../things/system_control.py | 48 ---------------
webapp/src/App.vue | 2 +-
.../aboutComponents/statusPane.vue | 2 +-
.../tabContentComponents/powerContent.vue | 6 +-
7 files changed, 57 insertions(+), 65 deletions(-)
rename src/openflexure_microscope_server/things/{settings_manager.py => system.py} (57%)
delete mode 100644 src/openflexure_microscope_server/things/system_control.py
diff --git a/ofm_config_full.json b/ofm_config_full.json
index 38a76685..1cfedb26 100644
--- a/ofm_config_full.json
+++ b/ofm_config_full.json
@@ -5,8 +5,7 @@
"/auto_recentre_stage/": "openflexure_microscope_server.things.auto_recentre_stage:RecentringThing",
"/autofocus/": "openflexure_microscope_server.things.autofocus:AutofocusThing",
"/camera_stage_mapping/": "openflexure_microscope_server.things.camera_stage_mapping:CameraStageMapper",
- "/system_control/": "openflexure_microscope_server.things.system_control:SystemControlThing",
- "/settings/": "openflexure_microscope_server.things.settings_manager:SettingsManager",
+ "/system/": "openflexure_microscope_server.things.system:OpenFlexureSystem",
"/smart_scan/": {
"class": "openflexure_microscope_server.things.smart_scan:SmartScanThing",
"kwargs": {
diff --git a/ofm_config_simulation.json b/ofm_config_simulation.json
index 9acf747b..a254fa85 100644
--- a/ofm_config_simulation.json
+++ b/ofm_config_simulation.json
@@ -5,8 +5,7 @@
"/auto_recentre_stage/": "openflexure_microscope_server.things.auto_recentre_stage:RecentringThing",
"/autofocus/": "openflexure_microscope_server.things.autofocus:AutofocusThing",
"/camera_stage_mapping/": "openflexure_microscope_server.things.camera_stage_mapping:CameraStageMapper",
- "/system_control/": "openflexure_microscope_server.things.system_control:SystemControlThing",
- "/settings/": "openflexure_microscope_server.things.settings_manager:SettingsManager",
+ "/system/": "openflexure_microscope_server.things.system:OpenFlexureSystem",
"/smart_scan/": {
"class": "openflexure_microscope_server.things.smart_scan:SmartScanThing",
"kwargs": {
diff --git a/src/openflexure_microscope_server/things/settings_manager.py b/src/openflexure_microscope_server/things/system.py
similarity index 57%
rename from src/openflexure_microscope_server/things/settings_manager.py
rename to src/openflexure_microscope_server/things/system.py
index df0e144d..bd3f23f9 100644
--- a/src/openflexure_microscope_server/things/settings_manager.py
+++ b/src/openflexure_microscope_server/things/system.py
@@ -1,25 +1,39 @@
-"""OpenFlexure Settings Management.
+"""OpenFlexure System.
-This module provides some settings management across the other Things, and
-for code that currently lives in clients but needs to persist settings on
-the server.
+A module to control the underlying microscope system and to expose information about
+the microscope, server, and thing states to the web API.
"""
from collections.abc import Mapping
from socket import gethostname
from typing import Optional
from uuid import UUID, uuid4
+import subprocess
+import os
+
+from pydantic import BaseModel
import labthings_fastapi as lt
from openflexure_microscope_server.utilities import VersionData, robust_version_strings
-class SettingsManager(lt.Thing):
- """Provides functionality to other Things about the current server state.
+class CommandOutput(BaseModel):
+ """A pydantic model passing the STDOUT and STDERR from a subprocess over HTTP."""
- The SettingsManager is used to get information the microscope ID, the hostname
- and the state of other Things.
+ output: str
+ error: str
+
+
+class OpenFlexureSystem(lt.Thing):
+ """Describe and control the OpenFlexure system.
+
+ This Things:
+
+ * Exposes information about the Microscope, Server, and Thing states to the web
+ API.
+ * Controls the underlying OS on the Raspberry Pi allowing shutdown and restarting
+ of the system.
"""
_microscope_id: Optional[str] = None
@@ -58,6 +72,34 @@ class SettingsManager(lt.Thing):
self._version_data = robust_version_strings()
return self._version_data
+ @lt.thing_property
+ def is_raspberrypi(self) -> bool:
+ """Return True if running on a Raspberry Pi."""
+ return os.path.exists("/usr/bin/raspi-config")
+
+ @lt.thing_action
+ def shutdown(self) -> CommandOutput:
+ """Attempt to shutdown the device."""
+ p = subprocess.Popen(
+ ["sudo", "shutdown", "-h", "now"],
+ stderr=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ )
+
+ out, err = p.communicate()
+ return CommandOutput(output=out, error=err)
+
+ @lt.thing_action
+ def reboot(self) -> CommandOutput:
+ """Attempt to reboot the device."""
+ p = subprocess.Popen(
+ ["sudo", "shutdown", "-r", "now"],
+ stderr=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ )
+ out, err = p.communicate()
+ return CommandOutput(output=out, error=err)
+
@lt.thing_action
def get_things_state(self, metadata_getter: lt.deps.GetThingStates) -> Mapping:
"""Metadata summarising the current state of all Things in the server."""
diff --git a/src/openflexure_microscope_server/things/system_control.py b/src/openflexure_microscope_server/things/system_control.py
deleted file mode 100644
index 82dc276a..00000000
--- a/src/openflexure_microscope_server/things/system_control.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""OpenFlexure Microscope system control Thing.
-
-This module defines a Thing that can shut down or restart the host computer.
-"""
-
-import subprocess
-import os
-import labthings_fastapi as lt
-from pydantic import BaseModel
-
-
-class CommandOutput(BaseModel):
- """A pydantic model passing the STDOUT and STDERR from a subprocess over HTTP."""
-
- output: str
- error: str
-
-
-class SystemControlThing(lt.Thing):
- """Attempt to shutdown the device."""
-
- @lt.thing_action
- def shutdown(self) -> CommandOutput:
- """Attempt to shutdown the device."""
- p = subprocess.Popen(
- ["sudo", "shutdown", "-h", "now"],
- stderr=subprocess.PIPE,
- stdout=subprocess.PIPE,
- )
-
- out, err = p.communicate()
- return CommandOutput(output=out, error=err)
-
- @lt.thing_property
- def is_raspberrypi() -> bool:
- """Return True if running on a Raspberry Pi."""
- return os.path.exists("/usr/bin/raspi-config")
-
- @lt.thing_action
- def reboot(self) -> CommandOutput:
- """Attempt to reboot the device."""
- p = subprocess.Popen(
- ["sudo", "shutdown", "-r", "now"],
- stderr=subprocess.PIPE,
- stdout=subprocess.PIPE,
- )
- out, err = p.communicate()
- return CommandOutput(output=out, error=err)
diff --git a/webapp/src/App.vue b/webapp/src/App.vue
index 97c46c19..3f76338e 100644
--- a/webapp/src/App.vue
+++ b/webapp/src/App.vue
@@ -236,7 +236,7 @@ export default {
}
}
try {
- let hostname = await this.readThingProperty("settings", "hostname");
+ let hostname = await this.readThingProperty("system", "hostname");
this.$store.commit("changeMicroscopeHostname", hostname);
document.title = `OpenFlexure Microscope: ${hostname}`;
} catch {
diff --git a/webapp/src/components/tabContentComponents/aboutComponents/statusPane.vue b/webapp/src/components/tabContentComponents/aboutComponents/statusPane.vue
index 6314666f..4d8d032d 100644
--- a/webapp/src/components/tabContentComponents/aboutComponents/statusPane.vue
+++ b/webapp/src/components/tabContentComponents/aboutComponents/statusPane.vue
@@ -76,7 +76,7 @@ export default {
async mounted() {
let version_data = await this.readThingProperty(
- "settings",
+ "system",
"version_data"
);
this.version = version_data.version;
diff --git a/webapp/src/components/tabContentComponents/powerContent.vue b/webapp/src/components/tabContentComponents/powerContent.vue
index 855174d8..b442a796 100644
--- a/webapp/src/components/tabContentComponents/powerContent.vue
+++ b/webapp/src/components/tabContentComponents/powerContent.vue
@@ -9,14 +9,14 @@