mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 03:26:45 +00:00
* Force label printing to background worker * Refactor "check_reload" state of machine registry - In line with plugin registry - More work can be done here (i.e. session caching) * Better handling of call_plugin_function * Wrapper for calling machine function * Use AttributeError instead * Simplify function offloading * Check plugin registry hash when reloading machine registry * Cleanup * Fixes * Adjust unit test * Cleanup * Allow running in foreground if background worker not running * Simplify call structure
This commit is contained in:
parent
75420fc97e
commit
af0a2822d1
@ -233,17 +233,21 @@ class BaseMachineType(
|
||||
# always fetch the machine_config if needed to ensure we get the newest reference
|
||||
from .models import MachineConfig
|
||||
|
||||
return MachineConfig.objects.get(pk=self.pk)
|
||||
return MachineConfig.objects.filter(pk=self.pk).first()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""The machines name."""
|
||||
return self.machine_config.name
|
||||
if config := self.machine_config:
|
||||
return config.name
|
||||
|
||||
@property
|
||||
def active(self):
|
||||
"""The machines active status."""
|
||||
return self.machine_config.active
|
||||
if config := self.machine_config:
|
||||
return config.active
|
||||
|
||||
return False
|
||||
|
||||
# --- hook functions
|
||||
def initialize(self):
|
||||
|
@ -5,7 +5,9 @@ from typing import Union, cast
|
||||
from uuid import UUID
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.db.utils import IntegrityError, OperationalError, ProgrammingError
|
||||
|
||||
from common.settings import get_global_setting, set_global_setting
|
||||
from InvenTree.helpers_mixin import get_shared_class_instance_state_mixin
|
||||
from machine.machine_type import BaseDriver, BaseMachineType
|
||||
|
||||
@ -29,6 +31,7 @@ class MachineRegistry(
|
||||
|
||||
self.base_drivers: list[type[BaseDriver]] = []
|
||||
|
||||
# Keep an internal hash of the machine registry state
|
||||
self._hash = None
|
||||
|
||||
@property
|
||||
@ -266,8 +269,14 @@ class MachineRegistry(
|
||||
"""Calculate a hash of the machine registry state."""
|
||||
from hashlib import md5
|
||||
|
||||
from plugin import registry as plugin_registry
|
||||
|
||||
data = md5()
|
||||
|
||||
# If the plugin registry has changed, the machine registry hash will change
|
||||
plugin_registry.update_plugin_hash()
|
||||
data.update(plugin_registry.registry_hash.encode())
|
||||
|
||||
for pk, machine in self.machines.items():
|
||||
data.update(str(pk).encode())
|
||||
try:
|
||||
@ -283,16 +292,84 @@ class MachineRegistry(
|
||||
if not self._hash:
|
||||
self._hash = self._calculate_registry_hash()
|
||||
|
||||
last_hash = self.get_shared_state('hash', None)
|
||||
try:
|
||||
reg_hash = get_global_setting('_MACHINE_REGISTRY_HASH', '', create=False)
|
||||
except Exception as exc:
|
||||
logger.exception('Failed to get machine registry hash: %s', str(exc))
|
||||
return False
|
||||
|
||||
if last_hash and last_hash != self._hash:
|
||||
if reg_hash and reg_hash != self._hash:
|
||||
logger.info('Machine registry has changed - reloading machines')
|
||||
self.reload_machines()
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _update_registry_hash(self):
|
||||
"""Save the current registry hash."""
|
||||
self._hash = self._calculate_registry_hash()
|
||||
self.set_shared_state('hash', self._hash)
|
||||
|
||||
try:
|
||||
old_hash = get_global_setting('_MACHINE_REGISTRY_HASH')
|
||||
except Exception:
|
||||
old_hash = None
|
||||
|
||||
if old_hash != self._hash:
|
||||
try:
|
||||
logger.info('Updating machine registry hash: %s', str(self._hash))
|
||||
set_global_setting('_MACHINE_REGISTRY_HASH', self._hash)
|
||||
except (IntegrityError, OperationalError, ProgrammingError):
|
||||
pass
|
||||
except Exception as exc:
|
||||
logger.exception('Failed to update machine registry hash: %s', str(exc))
|
||||
|
||||
def call_machine_function(
|
||||
self, machine_id: str, function_name: str, *args, **kwargs
|
||||
):
|
||||
"""Call a named function against a machine instance.
|
||||
|
||||
Arguments:
|
||||
machine_id: The UUID of the machine to call the function against
|
||||
function_name: The name of the function to call
|
||||
"""
|
||||
logger.info('call_machine_function: %s -> %s', machine_id, function_name)
|
||||
|
||||
raise_error = kwargs.pop('raise_error', True)
|
||||
|
||||
self._check_reload()
|
||||
|
||||
# Fetch the machine instance based on the provided UUID
|
||||
machine = self.get_machine(machine_id)
|
||||
|
||||
if not machine:
|
||||
if raise_error:
|
||||
raise AttributeError(f"Machine '{machine_id}' not found")
|
||||
return
|
||||
|
||||
# Fetch the driver instance based on the machine driver
|
||||
driver = machine.driver
|
||||
|
||||
if not driver:
|
||||
if raise_error:
|
||||
raise AttributeError(f"Machine '{machine_id}' has no specified driver")
|
||||
return
|
||||
|
||||
# The function must be registered against the driver
|
||||
func = getattr(driver, function_name)
|
||||
|
||||
if not func or not callable(func):
|
||||
if raise_error:
|
||||
raise AttributeError(
|
||||
f"Driver '{driver.SLUG}' has no callable method '{function_name}'"
|
||||
)
|
||||
return
|
||||
|
||||
return func(machine, *args, **kwargs)
|
||||
|
||||
|
||||
registry: MachineRegistry = MachineRegistry()
|
||||
|
||||
|
||||
def call_machine_function(machine_id: str, function: str, *args, **kwargs):
|
||||
"""Global helper function to call a specific function on a machine instance."""
|
||||
return registry.call_machine_function(machine_id, function, *args, **kwargs)
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
from typing import cast
|
||||
|
||||
from django.conf import settings
|
||||
from django.http import JsonResponse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
@ -12,7 +13,7 @@ from InvenTree.serializers import DependentField
|
||||
from InvenTree.tasks import offload_task
|
||||
from machine.machine_types import LabelPrinterBaseDriver, LabelPrinterMachine
|
||||
from plugin import InvenTreePlugin
|
||||
from plugin.machine import registry
|
||||
from plugin.machine import call_machine_function, registry
|
||||
from plugin.mixins import LabelPrintingMixin
|
||||
from report.models import LabelTemplate
|
||||
|
||||
@ -91,12 +92,15 @@ class InvenTreeLabelPlugin(LabelPrintingMixin, InvenTreePlugin):
|
||||
user=request.user,
|
||||
)
|
||||
|
||||
# execute the print job
|
||||
if driver.USE_BACKGROUND_WORKER is False:
|
||||
return driver.print_labels(machine, label, items, **print_kwargs)
|
||||
|
||||
offload_task(
|
||||
driver.print_labels, machine, label, items, group='plugin', **print_kwargs
|
||||
call_machine_function,
|
||||
machine.pk,
|
||||
'print_labels',
|
||||
label,
|
||||
items,
|
||||
force_sync=settings.TESTING or driver.USE_BACKGROUND_WORKER,
|
||||
group='plugin',
|
||||
**print_kwargs,
|
||||
)
|
||||
|
||||
return JsonResponse({
|
||||
|
@ -1,3 +1,10 @@
|
||||
from machine import BaseDriver, BaseMachineType, MachineStatus, registry
|
||||
from machine.registry import call_machine_function
|
||||
|
||||
__all__ = ['BaseDriver', 'BaseMachineType', 'MachineStatus', 'registry']
|
||||
__all__ = [
|
||||
'BaseDriver',
|
||||
'BaseMachineType',
|
||||
'MachineStatus',
|
||||
'call_machine_function',
|
||||
'registry',
|
||||
]
|
||||
|
@ -170,13 +170,22 @@ class PluginsRegistry:
|
||||
# Check if the registry needs to be reloaded
|
||||
self.check_reload()
|
||||
|
||||
raise_error = kwargs.pop('raise_error', True)
|
||||
|
||||
plugin = self.get_plugin(slug)
|
||||
|
||||
if not plugin:
|
||||
if raise_error:
|
||||
raise AttributeError(f"Plugin '{slug}' not found")
|
||||
return
|
||||
|
||||
plugin_func = getattr(plugin, func)
|
||||
|
||||
if not plugin_func or not callable(plugin_func):
|
||||
if raise_error:
|
||||
raise AttributeError(f"Plugin '{slug}' has no callable method '{func}'")
|
||||
return
|
||||
|
||||
return plugin_func(*args, **kwargs)
|
||||
|
||||
# region registry functions
|
||||
|
@ -65,12 +65,13 @@ class ExampleScheduledTaskPluginTests(TestCase):
|
||||
self.assertEqual(len(scheduled_plugin_tasks), 0)
|
||||
|
||||
def test_calling(self):
|
||||
"""Check if a function can be called without errors."""
|
||||
"""Test calling of plugin functions by name."""
|
||||
# Check with right parameters
|
||||
self.assertEqual(call_plugin_function('schedule', 'member_func'), False)
|
||||
|
||||
# Check with wrong key
|
||||
self.assertEqual(call_plugin_function('does_not_exist', 'member_func'), None)
|
||||
with self.assertRaises(AttributeError):
|
||||
call_plugin_function('does_not_exist', 'member_func'), None
|
||||
|
||||
|
||||
class ScheduledTaskPluginTests(TestCase):
|
||||
|
@ -511,7 +511,7 @@ export function MachineListTable({
|
||||
}, [machineDrivers, createFormMachineType]);
|
||||
|
||||
const createMachineForm = useCreateApiFormModal({
|
||||
title: t`Add machine`,
|
||||
title: t`Add Machine`,
|
||||
url: ApiEndpoints.machine_list,
|
||||
fields: {
|
||||
name: {},
|
||||
|
Loading…
x
Reference in New Issue
Block a user