Use semver library for checking sangaboard firmware version.
This commit is contained in:
parent
797dda3514
commit
240b7d1ba5
3 changed files with 83 additions and 31 deletions
|
|
@ -30,6 +30,7 @@ dependencies = [
|
||||||
"piexif",
|
"piexif",
|
||||||
"pydantic ~= 2.10.6",
|
"pydantic ~= 2.10.6",
|
||||||
"simplejpeg >= 1.8.2",
|
"simplejpeg >= 1.8.2",
|
||||||
|
"semver ~= 3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ from types import TracebackType
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
|
|
||||||
|
import semver
|
||||||
import sangaboard
|
import sangaboard
|
||||||
import labthings_fastapi as lt
|
import labthings_fastapi as lt
|
||||||
|
|
||||||
|
|
@ -17,7 +18,8 @@ from . import BaseStage
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
REQUIRED_VERSION = (1, 0, 4)
|
REQUIRED_VERSION = semver.Version.parse("1.0.0")
|
||||||
|
RECOMMENDED_VERSION = semver.Version.parse("1.0.4")
|
||||||
|
|
||||||
|
|
||||||
class SangaboardThing(BaseStage):
|
class SangaboardThing(BaseStage):
|
||||||
|
|
@ -89,40 +91,31 @@ class SangaboardThing(BaseStage):
|
||||||
)
|
)
|
||||||
|
|
||||||
def check_firmware(self) -> None:
|
def check_firmware(self) -> None:
|
||||||
"""Check the Sangaboard firmware version and log warnings or info messages.
|
"""Error/warn if firmware doesn't meet requirements/recommendations.
|
||||||
|
|
||||||
Logs a warning if the numeric firmware version is below ``REQUIRED_VERSION``.
|
Raise a Runtime Error if the version is below REQUIRED_VERSION
|
||||||
Logs an info message if the version ends with ``'-dev'``. Malformed version
|
|
||||||
strings trigger a warning.
|
Log a warning if the version is below RECOMMENDED_VERSION
|
||||||
"""
|
"""
|
||||||
with self.sangaboard() as sb:
|
with self.sangaboard() as sb:
|
||||||
version = sb.firmware_version
|
# This will raise a ValueError is if the firmware version cannot be parsed
|
||||||
|
# this error will stop the microscope booting as we cannot ensure valid
|
||||||
|
# firmware.
|
||||||
|
version = semver.Version.parse(sb.firmware_version)
|
||||||
|
|
||||||
# Remove '-dev' for numeric comparison
|
# Warn if version is below required
|
||||||
is_dev = version.endswith("-dev")
|
if version < REQUIRED_VERSION:
|
||||||
base_version = version[:-4] if is_dev else version
|
raise RuntimeError(
|
||||||
|
f"Sangaboard firmware version {version} is below the required "
|
||||||
# Parse the numeric part
|
f"{REQUIRED_VERSION}."
|
||||||
parts = base_version.split(".")
|
|
||||||
try:
|
|
||||||
major, minor, patch = (int(p) for p in parts)
|
|
||||||
except ValueError:
|
|
||||||
LOGGER.warning(
|
|
||||||
f"Unrecognized Sangaboard firmware version format: {version}"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
current = (major, minor, patch)
|
|
||||||
|
|
||||||
# Warn if version is below minimum
|
|
||||||
if current < REQUIRED_VERSION:
|
|
||||||
LOGGER.warning(
|
|
||||||
f"Sangaboard firmware version {version} is below the required 1.0.4."
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Log dev info
|
# Warn if version is below recommended
|
||||||
if is_dev:
|
if version < RECOMMENDED_VERSION:
|
||||||
LOGGER.info(f"Firmware version {version} is a development build.")
|
LOGGER.warning(
|
||||||
|
f"Sangaboard firmware version {version} is below the recommended "
|
||||||
|
f"{RECOMMENDED_VERSION}."
|
||||||
|
)
|
||||||
|
|
||||||
def _hardware_move_relative(
|
def _hardware_move_relative(
|
||||||
self,
|
self,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,14 @@
|
||||||
"""Tests for the Sangaboard thing."""
|
"""Tests for the Sangaboard thing."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from openflexure_microscope_server.things.stage.sangaboard import SangaboardThing
|
from openflexure_microscope_server.things.stage.sangaboard import (
|
||||||
|
SangaboardThing,
|
||||||
|
REQUIRED_VERSION,
|
||||||
|
RECOMMENDED_VERSION,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
@ -15,6 +21,58 @@ def mock_sanga_thing(mocker):
|
||||||
|
|
||||||
def test_check_old_firmware(mock_sanga_thing):
|
def test_check_old_firmware(mock_sanga_thing):
|
||||||
"""Check firmware prior to version 1 throws a Runtime Error."""
|
"""Check firmware prior to version 1 throws a Runtime Error."""
|
||||||
mock_sanga_thing._sangaboard.firmware_version.return_value = "0.1.1"
|
mock_sanga_thing._sangaboard.firmware_version = "0.1.1"
|
||||||
with pytest.raises(RuntimeError):
|
with pytest.raises(RuntimeError):
|
||||||
mock_sanga_thing.check_firmware()
|
mock_sanga_thing.check_firmware()
|
||||||
|
|
||||||
|
|
||||||
|
def test_firmware_before_recommended(mock_sanga_thing, caplog):
|
||||||
|
"""Check required version warns if it is lower than the recommended version."""
|
||||||
|
if REQUIRED_VERSION == RECOMMENDED_VERSION:
|
||||||
|
# If required is currently the same as recommended then this test isn't valid
|
||||||
|
return
|
||||||
|
mock_sanga_thing._sangaboard.firmware_version = str(REQUIRED_VERSION)
|
||||||
|
with caplog.at_level(logging.WARNING):
|
||||||
|
mock_sanga_thing.check_firmware()
|
||||||
|
assert len(caplog.records) == 1
|
||||||
|
msg = (
|
||||||
|
f"Sangaboard firmware version {REQUIRED_VERSION} is below the recommended "
|
||||||
|
f"{RECOMMENDED_VERSION}."
|
||||||
|
)
|
||||||
|
assert caplog.records[0].message == msg
|
||||||
|
|
||||||
|
|
||||||
|
def test_dev_is_before_recommended(mock_sanga_thing, caplog):
|
||||||
|
"""Check that a dev version of the recommended version warns."""
|
||||||
|
if REQUIRED_VERSION == RECOMMENDED_VERSION:
|
||||||
|
# If required is currently the same as recommended then this test isn't valid
|
||||||
|
return
|
||||||
|
dev_version = RECOMMENDED_VERSION.replace(prerelease="dev1")
|
||||||
|
mock_sanga_thing._sangaboard.firmware_version = str(dev_version)
|
||||||
|
with caplog.at_level(logging.WARNING):
|
||||||
|
mock_sanga_thing.check_firmware()
|
||||||
|
assert len(caplog.records) == 1
|
||||||
|
msg = (
|
||||||
|
f"Sangaboard firmware version {dev_version} is below the recommended "
|
||||||
|
f"{RECOMMENDED_VERSION}."
|
||||||
|
)
|
||||||
|
assert caplog.records[0].message == msg
|
||||||
|
|
||||||
|
|
||||||
|
def test_valid_firmware(mock_sanga_thing, caplog):
|
||||||
|
"""Check valid firmware doesn't error."""
|
||||||
|
versions = [
|
||||||
|
RECOMMENDED_VERSION, # Recommended
|
||||||
|
RECOMMENDED_VERSION.bump_major(), # Higher versions
|
||||||
|
RECOMMENDED_VERSION.bump_minor(),
|
||||||
|
RECOMMENDED_VERSION.bump_patch(),
|
||||||
|
RECOMMENDED_VERSION.bump_patch().replace(prerelease="dev1"),
|
||||||
|
]
|
||||||
|
# Note that dev1 will not warn in check version, but will warn in the underlying
|
||||||
|
# Sangaboard library as it is a pre-release.
|
||||||
|
|
||||||
|
for version in versions:
|
||||||
|
mock_sanga_thing._sangaboard.firmware_version = str(version)
|
||||||
|
with caplog.at_level(logging.WARNING):
|
||||||
|
mock_sanga_thing.check_firmware()
|
||||||
|
assert len(caplog.records) == 0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue