mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-18 04:49:13 +00:00
fix(core): register tom_thumb, accept frame_hold in the test double, wire api_v3's managers
Three independent fixes found while validating every plugin on a 256x64 rig. FontManager never registered tom_thumb even though assets/fonts/tom-thumb.bdf ships with the core, so every plugin offering it logged "Font family 'tom_thumb' not found" (16 warnings per countdown render) and had to carry a private loader to use a bundled font. Closes #524. VisualTestDisplayManager.set_scrolling_state() lacked the frame_hold parameter that DisplayManager gained, so any plugin passing it died with TypeError at render time and failed every size. Nine plugins now make that call; ledmatrix-stocks and ledmatrix-leaderboard were failing outright and the other seven only passed because their scroll path was unreachable without data. Closes #525. api_v3 declared module-level config_manager/plugin_manager = None that nothing ever assigned -- app.py sets the blueprint attributes, which the other 150+ call sites use. Three sites read the decoys, so /health reported the config unreadable and the plugin system uninitialised (making "degraded" permanent and unreachable-by-design) and /display/current fell back to a hardcoded 128x64 on every rig. The decoys are removed rather than assigned, so a bare name is now a NameError at test time instead of a silent None. The same function's first-call uptime was computed from two separate clock reads and came out negative. Closes #529. Verified on the rig: both previously-failing plugins render, the tom_thumb warnings are gone, /health reports "healthy" with all three checks passing, and /display/current reports the real 256x64.
This commit is contained in:
+2
-1
@@ -96,7 +96,8 @@ class FontManager:
|
|||||||
self.common_fonts = {
|
self.common_fonts = {
|
||||||
"press_start": "assets/fonts/PressStart2P-Regular.ttf",
|
"press_start": "assets/fonts/PressStart2P-Regular.ttf",
|
||||||
"four_by_six": "assets/fonts/4x6-font.ttf",
|
"four_by_six": "assets/fonts/4x6-font.ttf",
|
||||||
"five_by_seven": "assets/fonts/5x7.bdf"
|
"five_by_seven": "assets/fonts/5x7.bdf",
|
||||||
|
"tom_thumb": "assets/fonts/tom-thumb.bdf"
|
||||||
# Note: cozette_bdf removed - font file not available
|
# Note: cozette_bdf removed - font file not available
|
||||||
# To re-enable: download cozette.bdf from https://github.com/the-moonwitch/Cozette
|
# To re-enable: download cozette.bdf from https://github.com/the-moonwitch/Cozette
|
||||||
# and add: "cozette_bdf": "assets/fonts/cozette.bdf"
|
# and add: "cozette_bdf": "assets/fonts/cozette.bdf"
|
||||||
|
|||||||
@@ -506,9 +506,16 @@ class VisualTestDisplayManager:
|
|||||||
# Scrolling state (no-op interface compat)
|
# Scrolling state (no-op interface compat)
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
def set_scrolling_state(self, is_scrolling: bool):
|
def set_scrolling_state(self, is_scrolling: bool, frame_hold: int = 1):
|
||||||
"""Set the current scrolling state (no-op for testing)."""
|
"""Set the current scrolling state (no-op for testing).
|
||||||
|
|
||||||
|
``frame_hold`` mirrors DisplayManager.set_scrolling_state so a plugin
|
||||||
|
that paces its scroll can be rendered here. Without it every such
|
||||||
|
plugin raised TypeError at render time and failed every size, which is
|
||||||
|
invisible until a plugin happens to pass the argument.
|
||||||
|
"""
|
||||||
self._scrolling_state['is_scrolling'] = is_scrolling
|
self._scrolling_state['is_scrolling'] = is_scrolling
|
||||||
|
self._scrolling_state['frame_hold'] = frame_hold
|
||||||
if is_scrolling:
|
if is_scrolling:
|
||||||
self._scrolling_state['last_scroll_activity'] = time.time()
|
self._scrolling_state['last_scroll_activity'] = time.time()
|
||||||
|
|
||||||
|
|||||||
@@ -78,8 +78,11 @@ def _scrub_git_remote_url(url: str) -> str:
|
|||||||
return url
|
return url
|
||||||
|
|
||||||
# Will be initialized when blueprint is registered
|
# Will be initialized when blueprint is registered
|
||||||
config_manager = None
|
# NOTE: the managers live on the blueprint object (app.py sets
|
||||||
plugin_manager = None
|
# api_v3.config_manager / api_v3.plugin_manager). Deliberately not
|
||||||
|
# mirrored as module globals: a bare `config_manager` used to resolve to
|
||||||
|
# a None that was never assigned, which silently disabled the /health
|
||||||
|
# checks and made /display/current fall back to a hardcoded 128x64.
|
||||||
plugin_store_manager = None
|
plugin_store_manager = None
|
||||||
saved_repositories_manager = None
|
saved_repositories_manager = None
|
||||||
cache_manager = None
|
cache_manager = None
|
||||||
@@ -1598,11 +1601,16 @@ def get_health():
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Check web interface service
|
# Check web interface service
|
||||||
|
# Stamp the start time before measuring against it -- reading it with a
|
||||||
|
# fallback of time.time() and only assigning afterwards made the very
|
||||||
|
# first call subtract two separate clock reads, reporting a small
|
||||||
|
# negative uptime.
|
||||||
|
if not hasattr(get_health, '_start_time'):
|
||||||
|
get_health._start_time = time.time()
|
||||||
health_status['services']['web_interface'] = {
|
health_status['services']['web_interface'] = {
|
||||||
'status': 'running',
|
'status': 'running',
|
||||||
'uptime_seconds': time.time() - (getattr(get_health, '_start_time', time.time()))
|
'uptime_seconds': time.time() - get_health._start_time
|
||||||
}
|
}
|
||||||
get_health._start_time = getattr(get_health, '_start_time', time.time())
|
|
||||||
|
|
||||||
# Check display service
|
# Check display service
|
||||||
display_service_status = _get_display_service_status()
|
display_service_status = _get_display_service_status()
|
||||||
@@ -1613,8 +1621,8 @@ def get_health():
|
|||||||
|
|
||||||
# Check config file accessibility
|
# Check config file accessibility
|
||||||
try:
|
try:
|
||||||
if config_manager:
|
if api_v3.config_manager:
|
||||||
test_config = config_manager.load_config()
|
test_config = api_v3.config_manager.load_config()
|
||||||
health_status['checks']['config_file'] = {
|
health_status['checks']['config_file'] = {
|
||||||
'status': 'accessible',
|
'status': 'accessible',
|
||||||
'readable': True
|
'readable': True
|
||||||
@@ -1633,9 +1641,9 @@ def get_health():
|
|||||||
|
|
||||||
# Check plugin system
|
# Check plugin system
|
||||||
try:
|
try:
|
||||||
if plugin_manager:
|
if api_v3.plugin_manager:
|
||||||
# Try to discover plugins (lightweight check)
|
# Try to discover plugins (lightweight check)
|
||||||
plugin_count = len(plugin_manager.get_available_plugins()) if hasattr(plugin_manager, 'get_available_plugins') else 0
|
plugin_count = len(api_v3.plugin_manager.get_available_plugins()) if hasattr(api_v3.plugin_manager, 'get_available_plugins') else 0
|
||||||
health_status['checks']['plugin_system'] = {
|
health_status['checks']['plugin_system'] = {
|
||||||
'status': 'operational',
|
'status': 'operational',
|
||||||
'plugin_count': plugin_count
|
'plugin_count': plugin_count
|
||||||
@@ -2468,8 +2476,8 @@ def get_display_current():
|
|||||||
|
|
||||||
# Get display dimensions from config
|
# Get display dimensions from config
|
||||||
try:
|
try:
|
||||||
if config_manager:
|
if api_v3.config_manager:
|
||||||
main_config = config_manager.load_config()
|
main_config = api_v3.config_manager.load_config()
|
||||||
hardware_config = main_config.get('display', {}).get('hardware', {})
|
hardware_config = main_config.get('display', {}).get('hardware', {})
|
||||||
cols = hardware_config.get('cols', 64)
|
cols = hardware_config.get('cols', 64)
|
||||||
chain_length = hardware_config.get('chain_length', 2)
|
chain_length = hardware_config.get('chain_length', 2)
|
||||||
|
|||||||
Reference in New Issue
Block a user