Merge SettingsManager and SystemControl Things.

This commit is contained in:
Julian Stirling 2025-07-13 11:14:20 +01:00
parent bed59c051b
commit be94df7c8a
7 changed files with 57 additions and 65 deletions

View file

@ -5,8 +5,7 @@
"/auto_recentre_stage/": "openflexure_microscope_server.things.auto_recentre_stage:RecentringThing", "/auto_recentre_stage/": "openflexure_microscope_server.things.auto_recentre_stage:RecentringThing",
"/autofocus/": "openflexure_microscope_server.things.autofocus:AutofocusThing", "/autofocus/": "openflexure_microscope_server.things.autofocus:AutofocusThing",
"/camera_stage_mapping/": "openflexure_microscope_server.things.camera_stage_mapping:CameraStageMapper", "/camera_stage_mapping/": "openflexure_microscope_server.things.camera_stage_mapping:CameraStageMapper",
"/system_control/": "openflexure_microscope_server.things.system_control:SystemControlThing", "/system/": "openflexure_microscope_server.things.system:OpenFlexureSystem",
"/settings/": "openflexure_microscope_server.things.settings_manager:SettingsManager",
"/smart_scan/": { "/smart_scan/": {
"class": "openflexure_microscope_server.things.smart_scan:SmartScanThing", "class": "openflexure_microscope_server.things.smart_scan:SmartScanThing",
"kwargs": { "kwargs": {

View file

@ -5,8 +5,7 @@
"/auto_recentre_stage/": "openflexure_microscope_server.things.auto_recentre_stage:RecentringThing", "/auto_recentre_stage/": "openflexure_microscope_server.things.auto_recentre_stage:RecentringThing",
"/autofocus/": "openflexure_microscope_server.things.autofocus:AutofocusThing", "/autofocus/": "openflexure_microscope_server.things.autofocus:AutofocusThing",
"/camera_stage_mapping/": "openflexure_microscope_server.things.camera_stage_mapping:CameraStageMapper", "/camera_stage_mapping/": "openflexure_microscope_server.things.camera_stage_mapping:CameraStageMapper",
"/system_control/": "openflexure_microscope_server.things.system_control:SystemControlThing", "/system/": "openflexure_microscope_server.things.system:OpenFlexureSystem",
"/settings/": "openflexure_microscope_server.things.settings_manager:SettingsManager",
"/smart_scan/": { "/smart_scan/": {
"class": "openflexure_microscope_server.things.smart_scan:SmartScanThing", "class": "openflexure_microscope_server.things.smart_scan:SmartScanThing",
"kwargs": { "kwargs": {

View file

@ -1,25 +1,39 @@
"""OpenFlexure Settings Management. """OpenFlexure System.
This module provides some settings management across the other Things, and A module to control the underlying microscope system and to expose information about
for code that currently lives in clients but needs to persist settings on the microscope, server, and thing states to the web API.
the server.
""" """
from collections.abc import Mapping from collections.abc import Mapping
from socket import gethostname from socket import gethostname
from typing import Optional from typing import Optional
from uuid import UUID, uuid4 from uuid import UUID, uuid4
import subprocess
import os
from pydantic import BaseModel
import labthings_fastapi as lt import labthings_fastapi as lt
from openflexure_microscope_server.utilities import VersionData, robust_version_strings from openflexure_microscope_server.utilities import VersionData, robust_version_strings
class SettingsManager(lt.Thing): class CommandOutput(BaseModel):
"""Provides functionality to other Things about the current server state. """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 output: str
and the state of other Things. 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 _microscope_id: Optional[str] = None
@ -58,6 +72,34 @@ class SettingsManager(lt.Thing):
self._version_data = robust_version_strings() self._version_data = robust_version_strings()
return self._version_data 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 @lt.thing_action
def get_things_state(self, metadata_getter: lt.deps.GetThingStates) -> Mapping: def get_things_state(self, metadata_getter: lt.deps.GetThingStates) -> Mapping:
"""Metadata summarising the current state of all Things in the server.""" """Metadata summarising the current state of all Things in the server."""

View file

@ -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)

View file

@ -236,7 +236,7 @@ export default {
} }
} }
try { try {
let hostname = await this.readThingProperty("settings", "hostname"); let hostname = await this.readThingProperty("system", "hostname");
this.$store.commit("changeMicroscopeHostname", hostname); this.$store.commit("changeMicroscopeHostname", hostname);
document.title = `OpenFlexure Microscope: ${hostname}`; document.title = `OpenFlexure Microscope: ${hostname}`;
} catch { } catch {

View file

@ -76,7 +76,7 @@ export default {
async mounted() { async mounted() {
let version_data = await this.readThingProperty( let version_data = await this.readThingProperty(
"settings", "system",
"version_data" "version_data"
); );
this.version = version_data.version; this.version = version_data.version;

View file

@ -9,14 +9,14 @@
</p> </p>
<div class="buttons"> <div class="buttons">
<button <button
v-show="'shutdown' in things.system_control.actions" v-show="'shutdown' in things.system.actions"
class="uk-button uk-button-primary uk-width-1-3" class="uk-button uk-button-primary uk-width-1-3"
@click="systemRequest('shutdown')" @click="systemRequest('shutdown')"
> >
Shutdown Shutdown
</button> </button>
<button <button
v-show="'reboot' in things.system_control.actions" v-show="'reboot' in things.system.actions"
class="uk-button uk-button-primary uk-width-1-3" class="uk-button uk-button-primary uk-width-1-3"
@click="systemRequest('reboot')" @click="systemRequest('reboot')"
> >
@ -55,7 +55,7 @@ export default {
this.$store.commit("wot/deleteAllThingDescriptions"); this.$store.commit("wot/deleteAllThingDescriptions");
// Post and silence errors // Post and silence errors
axios axios
.post(this.thingActionUrl("system_control", action)) .post(this.thingActionUrl("system", action))
.catch(() => {}); .catch(() => {});
}, },
() => {} () => {}