mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 12:06:44 +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
|
# always fetch the machine_config if needed to ensure we get the newest reference
|
||||||
from .models import MachineConfig
|
from .models import MachineConfig
|
||||||
|
|
||||||
return MachineConfig.objects.get(pk=self.pk)
|
return MachineConfig.objects.filter(pk=self.pk).first()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""The machines name."""
|
"""The machines name."""
|
||||||
return self.machine_config.name
|
if config := self.machine_config:
|
||||||
|
return config.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def active(self):
|
def active(self):
|
||||||
"""The machines active status."""
|
"""The machines active status."""
|
||||||
return self.machine_config.active
|
if config := self.machine_config:
|
||||||
|
return config.active
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
# --- hook functions
|
# --- hook functions
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
|
@ -5,7 +5,9 @@ from typing import Union, cast
|
|||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from django.core.cache import cache
|
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 InvenTree.helpers_mixin import get_shared_class_instance_state_mixin
|
||||||
from machine.machine_type import BaseDriver, BaseMachineType
|
from machine.machine_type import BaseDriver, BaseMachineType
|
||||||
|
|
||||||
@ -29,6 +31,7 @@ class MachineRegistry(
|
|||||||
|
|
||||||
self.base_drivers: list[type[BaseDriver]] = []
|
self.base_drivers: list[type[BaseDriver]] = []
|
||||||
|
|
||||||
|
# Keep an internal hash of the machine registry state
|
||||||
self._hash = None
|
self._hash = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -266,8 +269,14 @@ class MachineRegistry(
|
|||||||
"""Calculate a hash of the machine registry state."""
|
"""Calculate a hash of the machine registry state."""
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
|
|
||||||
|
from plugin import registry as plugin_registry
|
||||||
|
|
||||||
data = md5()
|
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():
|
for pk, machine in self.machines.items():
|
||||||
data.update(str(pk).encode())
|
data.update(str(pk).encode())
|
||||||
try:
|
try:
|
||||||
@ -283,16 +292,84 @@ class MachineRegistry(
|
|||||||
if not self._hash:
|
if not self._hash:
|
||||||
self._hash = self._calculate_registry_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')
|
logger.info('Machine registry has changed - reloading machines')
|
||||||
self.reload_machines()
|
self.reload_machines()
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def _update_registry_hash(self):
|
def _update_registry_hash(self):
|
||||||
"""Save the current registry hash."""
|
"""Save the current registry hash."""
|
||||||
self._hash = self._calculate_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()
|
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 typing import cast
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
@ -12,7 +13,7 @@ from InvenTree.serializers import DependentField
|
|||||||
from InvenTree.tasks import offload_task
|
from InvenTree.tasks import offload_task
|
||||||
from machine.machine_types import LabelPrinterBaseDriver, LabelPrinterMachine
|
from machine.machine_types import LabelPrinterBaseDriver, LabelPrinterMachine
|
||||||
from plugin import InvenTreePlugin
|
from plugin import InvenTreePlugin
|
||||||
from plugin.machine import registry
|
from plugin.machine import call_machine_function, registry
|
||||||
from plugin.mixins import LabelPrintingMixin
|
from plugin.mixins import LabelPrintingMixin
|
||||||
from report.models import LabelTemplate
|
from report.models import LabelTemplate
|
||||||
|
|
||||||
@ -91,12 +92,15 @@ class InvenTreeLabelPlugin(LabelPrintingMixin, InvenTreePlugin):
|
|||||||
user=request.user,
|
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(
|
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({
|
return JsonResponse({
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
from machine import BaseDriver, BaseMachineType, MachineStatus, registry
|
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
|
# Check if the registry needs to be reloaded
|
||||||
self.check_reload()
|
self.check_reload()
|
||||||
|
|
||||||
|
raise_error = kwargs.pop('raise_error', True)
|
||||||
|
|
||||||
plugin = self.get_plugin(slug)
|
plugin = self.get_plugin(slug)
|
||||||
|
|
||||||
if not plugin:
|
if not plugin:
|
||||||
|
if raise_error:
|
||||||
|
raise AttributeError(f"Plugin '{slug}' not found")
|
||||||
return
|
return
|
||||||
|
|
||||||
plugin_func = getattr(plugin, func)
|
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)
|
return plugin_func(*args, **kwargs)
|
||||||
|
|
||||||
# region registry functions
|
# region registry functions
|
||||||
|
@ -65,12 +65,13 @@ class ExampleScheduledTaskPluginTests(TestCase):
|
|||||||
self.assertEqual(len(scheduled_plugin_tasks), 0)
|
self.assertEqual(len(scheduled_plugin_tasks), 0)
|
||||||
|
|
||||||
def test_calling(self):
|
def test_calling(self):
|
||||||
"""Check if a function can be called without errors."""
|
"""Test calling of plugin functions by name."""
|
||||||
# Check with right parameters
|
# Check with right parameters
|
||||||
self.assertEqual(call_plugin_function('schedule', 'member_func'), False)
|
self.assertEqual(call_plugin_function('schedule', 'member_func'), False)
|
||||||
|
|
||||||
# Check with wrong key
|
# 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):
|
class ScheduledTaskPluginTests(TestCase):
|
||||||
|
@ -511,7 +511,7 @@ export function MachineListTable({
|
|||||||
}, [machineDrivers, createFormMachineType]);
|
}, [machineDrivers, createFormMachineType]);
|
||||||
|
|
||||||
const createMachineForm = useCreateApiFormModal({
|
const createMachineForm = useCreateApiFormModal({
|
||||||
title: t`Add machine`,
|
title: t`Add Machine`,
|
||||||
url: ApiEndpoints.machine_list,
|
url: ApiEndpoints.machine_list,
|
||||||
fields: {
|
fields: {
|
||||||
name: {},
|
name: {},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user