mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-16 20:15:44 +00:00
Fix machine request pickeling and settings (#6772)
* Fix machine request pickeling * fix precommit * fix: shared state between workers and main thread for machine registry * remove last usage of legacy PUI form framework to fix machine edit/delete modal * reset cache before initialization * update documentation * fix: invalidating cache * implement machine registry hash to check if a reload is required * trigger: ci * fix: request bug * fix: test * trigger: ci * add clear errors and improve restart hook * auto initialize not initialized machines when changing active state * fix: tests
This commit is contained in:
@ -2,8 +2,10 @@
|
||||
|
||||
import inspect
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
|
||||
from plugin import registry as plg_registry
|
||||
|
||||
@ -104,3 +106,37 @@ class ClassProviderMixin:
|
||||
except ValueError:
|
||||
# Path(...).relative_to throws an ValueError if its not relative to the InvenTree source base dir
|
||||
return False
|
||||
|
||||
|
||||
def get_shared_class_instance_state_mixin(get_state_key: Callable[[type], str]):
|
||||
"""Get a mixin class that provides shared state for classes across the main application and worker.
|
||||
|
||||
Arguments:
|
||||
get_state_key: A function that returns the key for the shared state when given a class instance.
|
||||
"""
|
||||
|
||||
class SharedClassStateMixinClass:
|
||||
"""Mixin to provide shared state for classes across the main application and worker."""
|
||||
|
||||
def set_shared_state(self, key: str, value: Any):
|
||||
"""Set a shared state value for this machine.
|
||||
|
||||
Arguments:
|
||||
key: The key for the shared state
|
||||
value: The value to set
|
||||
"""
|
||||
cache.set(self._get_key(key), value, timeout=None)
|
||||
|
||||
def get_shared_state(self, key: str, default=None):
|
||||
"""Get a shared state value for this machine.
|
||||
|
||||
Arguments:
|
||||
key: The key for the shared state
|
||||
"""
|
||||
return cache.get(self._get_key(key)) or default
|
||||
|
||||
def _get_key(self, key: str):
|
||||
"""Get the key for this class instance."""
|
||||
return f'{get_state_key(self)}:{key}'
|
||||
|
||||
return SharedClassStateMixinClass
|
||||
|
@ -26,7 +26,6 @@ class MachineConfig(AppConfig):
|
||||
if (
|
||||
not canAppAccessDatabase(allow_test=True)
|
||||
or not isPluginRegistryLoaded()
|
||||
or not isInMainThread()
|
||||
or isRunningMigrations()
|
||||
or isImportingData()
|
||||
):
|
||||
@ -37,7 +36,7 @@ class MachineConfig(AppConfig):
|
||||
|
||||
try:
|
||||
logger.info('Loading InvenTree machines')
|
||||
registry.initialize()
|
||||
registry.initialize(main=isInMainThread())
|
||||
except (OperationalError, ProgrammingError):
|
||||
# Database might not yet be ready
|
||||
logger.warn('Database was not ready for initializing machines')
|
||||
|
@ -3,7 +3,11 @@
|
||||
from typing import TYPE_CHECKING, Any, Literal, Union
|
||||
|
||||
from generic.states import StatusCode
|
||||
from InvenTree.helpers_mixin import ClassProviderMixin, ClassValidationMixin
|
||||
from InvenTree.helpers_mixin import (
|
||||
ClassProviderMixin,
|
||||
ClassValidationMixin,
|
||||
get_shared_class_instance_state_mixin,
|
||||
)
|
||||
|
||||
# Import only for typechecking, otherwise this throws cyclic import errors
|
||||
if TYPE_CHECKING:
|
||||
@ -44,7 +48,11 @@ class MachineStatus(StatusCode):
|
||||
"""
|
||||
|
||||
|
||||
class BaseDriver(ClassValidationMixin, ClassProviderMixin):
|
||||
class BaseDriver(
|
||||
ClassValidationMixin,
|
||||
ClassProviderMixin,
|
||||
get_shared_class_instance_state_mixin(lambda x: f'machine:driver:{x.SLUG}'),
|
||||
):
|
||||
"""Base class for all machine drivers.
|
||||
|
||||
Attributes:
|
||||
@ -69,8 +77,6 @@ class BaseDriver(ClassValidationMixin, ClassProviderMixin):
|
||||
"""Base driver __init__ method."""
|
||||
super().__init__()
|
||||
|
||||
self.errors: list[Union[str, Exception]] = []
|
||||
|
||||
def init_driver(self):
|
||||
"""This method gets called after all machines are created and can be used to initialize the driver.
|
||||
|
||||
@ -133,10 +139,20 @@ class BaseDriver(ClassValidationMixin, ClassProviderMixin):
|
||||
Arguments:
|
||||
error: Exception or string
|
||||
"""
|
||||
self.errors.append(error)
|
||||
self.set_shared_state('errors', self.errors + [error])
|
||||
|
||||
# --- state getters/setters
|
||||
@property
|
||||
def errors(self) -> list[Union[str, Exception]]:
|
||||
"""List of driver errors."""
|
||||
return self.get_shared_state('errors', [])
|
||||
|
||||
|
||||
class BaseMachineType(ClassValidationMixin, ClassProviderMixin):
|
||||
class BaseMachineType(
|
||||
ClassValidationMixin,
|
||||
ClassProviderMixin,
|
||||
get_shared_class_instance_state_mixin(lambda x: f'machine:machine:{x.pk}'),
|
||||
):
|
||||
"""Base class for machine types.
|
||||
|
||||
Attributes:
|
||||
@ -178,12 +194,6 @@ class BaseMachineType(ClassValidationMixin, ClassProviderMixin):
|
||||
from machine import registry
|
||||
from machine.models import MachineSetting
|
||||
|
||||
self.errors: list[Union[str, Exception]] = []
|
||||
self.initialized = False
|
||||
|
||||
self.status = self.default_machine_status
|
||||
self.status_text: str = ''
|
||||
|
||||
self.pk = machine_config.pk
|
||||
self.driver = registry.get_driver_instance(machine_config.driver)
|
||||
|
||||
@ -208,8 +218,6 @@ class BaseMachineType(ClassValidationMixin, ClassProviderMixin):
|
||||
(self.driver_settings, MachineSetting.ConfigType.DRIVER),
|
||||
]
|
||||
|
||||
self.restart_required = False
|
||||
|
||||
def __str__(self):
|
||||
"""String representation of a machine."""
|
||||
return f'{self.name}'
|
||||
@ -272,16 +280,32 @@ class BaseMachineType(ClassValidationMixin, ClassProviderMixin):
|
||||
|
||||
try:
|
||||
self.driver.update_machine(old_state, self)
|
||||
|
||||
# check if the active state has changed and initialize the machine if necessary
|
||||
if old_state['active'] != self.active:
|
||||
if self.initialized is False and self.active is True:
|
||||
self.initialize()
|
||||
elif self.initialized is True and self.active is False:
|
||||
self.initialized = False
|
||||
except Exception as e:
|
||||
self.handle_error(e)
|
||||
|
||||
def restart(self):
|
||||
"""Machine restart function, can be used to manually restart the machine from the admin ui."""
|
||||
"""Machine restart function, can be used to manually restart the machine from the admin ui.
|
||||
|
||||
This will first reset the machines state (errors, status, status_text) and then call the drivers restart function.
|
||||
"""
|
||||
if self.driver is None:
|
||||
return
|
||||
|
||||
try:
|
||||
# reset the machine state
|
||||
self.restart_required = False
|
||||
self.reset_errors()
|
||||
self.set_status(self.default_machine_status)
|
||||
self.set_status_text('')
|
||||
|
||||
# call the driver restart function
|
||||
self.driver.restart_machine(self)
|
||||
except Exception as e:
|
||||
self.handle_error(e)
|
||||
@ -293,7 +317,11 @@ class BaseMachineType(ClassValidationMixin, ClassProviderMixin):
|
||||
Arguments:
|
||||
error: Exception or string
|
||||
"""
|
||||
self.errors.append(error)
|
||||
self.set_shared_state('errors', self.errors + [error])
|
||||
|
||||
def reset_errors(self):
|
||||
"""Helper function for resetting the error list for a machine."""
|
||||
self.set_shared_state('errors', [])
|
||||
|
||||
def get_setting(
|
||||
self, key: str, config_type_str: Literal['M', 'D'], cache: bool = False
|
||||
@ -364,7 +392,7 @@ class BaseMachineType(ClassValidationMixin, ClassProviderMixin):
|
||||
Arguments:
|
||||
status: The new MachineStatus code to set
|
||||
"""
|
||||
self.status = status
|
||||
self.set_shared_state('status', status.value)
|
||||
|
||||
def set_status_text(self, status_text: str):
|
||||
"""Set the machine status text. It can be any arbitrary text.
|
||||
@ -372,4 +400,39 @@ class BaseMachineType(ClassValidationMixin, ClassProviderMixin):
|
||||
Arguments:
|
||||
status_text: The new status text to set
|
||||
"""
|
||||
self.status_text = status_text
|
||||
self.set_shared_state('status_text', status_text)
|
||||
|
||||
# --- state getters/setters
|
||||
@property
|
||||
def initialized(self) -> bool:
|
||||
"""Initialized state of the machine."""
|
||||
return self.get_shared_state('initialized', False)
|
||||
|
||||
@initialized.setter
|
||||
def initialized(self, value: bool):
|
||||
self.set_shared_state('initialized', value)
|
||||
|
||||
@property
|
||||
def restart_required(self) -> bool:
|
||||
"""Restart required state of the machine."""
|
||||
return self.get_shared_state('restart_required', False)
|
||||
|
||||
@restart_required.setter
|
||||
def restart_required(self, value: bool):
|
||||
self.set_shared_state('restart_required', value)
|
||||
|
||||
@property
|
||||
def errors(self) -> list[Union[str, Exception]]:
|
||||
"""List of machine errors."""
|
||||
return self.get_shared_state('errors', [])
|
||||
|
||||
@property
|
||||
def status(self) -> MachineStatus:
|
||||
"""Machine status code."""
|
||||
status_code = self.get_shared_state('status', self.default_machine_status.value)
|
||||
return self.MACHINE_STATUS(status_code)
|
||||
|
||||
@property
|
||||
def status_text(self) -> str:
|
||||
"""Machine status text."""
|
||||
return self.get_shared_state('status_text', '')
|
||||
|
@ -2,9 +2,10 @@
|
||||
|
||||
from typing import Union, cast
|
||||
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.db import models
|
||||
from django.db.models.query import QuerySet
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
from django.http import HttpRequest, HttpResponse, JsonResponse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from PIL.Image import Image
|
||||
@ -34,7 +35,6 @@ class LabelPrinterBaseDriver(BaseDriver):
|
||||
machine: 'LabelPrinterMachine',
|
||||
label: LabelTemplate,
|
||||
item: models.Model,
|
||||
request: Request,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
"""Print a single label with the provided template and item.
|
||||
@ -43,7 +43,6 @@ class LabelPrinterBaseDriver(BaseDriver):
|
||||
machine: The LabelPrintingMachine instance that should be used for printing
|
||||
label: The LabelTemplate object to use for printing
|
||||
item: The database item to print (e.g. StockItem instance)
|
||||
request: The HTTP request object which triggered this print job
|
||||
|
||||
Keyword Arguments:
|
||||
printing_options (dict): The printing options set for this print job defined in the PrintingOptionsSerializer
|
||||
@ -57,8 +56,7 @@ class LabelPrinterBaseDriver(BaseDriver):
|
||||
self,
|
||||
machine: 'LabelPrinterMachine',
|
||||
label: LabelTemplate,
|
||||
items: QuerySet,
|
||||
request: Request,
|
||||
items: QuerySet[models.Model],
|
||||
**kwargs,
|
||||
) -> Union[None, JsonResponse]:
|
||||
"""Print one or more labels with the provided template and items.
|
||||
@ -67,7 +65,6 @@ class LabelPrinterBaseDriver(BaseDriver):
|
||||
machine: The LabelPrintingMachine instance that should be used for printing
|
||||
label: The LabelTemplate object to use for printing
|
||||
items: The list of database items to print (e.g. StockItem instances)
|
||||
request: The HTTP request object which triggered this print job
|
||||
|
||||
Keyword Arguments:
|
||||
printing_options (dict): The printing options set for this print job defined in the PrintingOptionsSerializer
|
||||
@ -81,7 +78,7 @@ class LabelPrinterBaseDriver(BaseDriver):
|
||||
but this can be overridden by the particular driver.
|
||||
"""
|
||||
for item in items:
|
||||
self.print_label(machine, label, item, request, **kwargs)
|
||||
self.print_label(machine, label, item, **kwargs)
|
||||
|
||||
def get_printers(
|
||||
self, label: LabelTemplate, items: QuerySet, **kwargs
|
||||
@ -123,56 +120,50 @@ class LabelPrinterBaseDriver(BaseDriver):
|
||||
return cast(LabelPrintingMixin, plg)
|
||||
|
||||
def render_to_pdf(
|
||||
self, label: LabelTemplate, item: models.Model, request: Request, **kwargs
|
||||
self, label: LabelTemplate, item: models.Model, **kwargs
|
||||
) -> HttpResponse:
|
||||
"""Helper method to render a label to PDF format for a specific item.
|
||||
|
||||
Arguments:
|
||||
label: The LabelTemplate object to render
|
||||
item: The item to render the label with
|
||||
request: The HTTP request object which triggered this print job
|
||||
"""
|
||||
response = self.machine_plugin.render_to_pdf(label, item, request, **kwargs)
|
||||
return response
|
||||
request = self._get_dummy_request()
|
||||
return self.machine_plugin.render_to_pdf(label, item, request, **kwargs)
|
||||
|
||||
def render_to_pdf_data(
|
||||
self, label: LabelTemplate, item: models.Model, request: Request, **kwargs
|
||||
self, label: LabelTemplate, item: models.Model, **kwargs
|
||||
) -> bytes:
|
||||
"""Helper method to render a label to PDF and return it as bytes for a specific item.
|
||||
|
||||
Arguments:
|
||||
label: The LabelTemplate object to render
|
||||
item: The item to render the label with
|
||||
request: The HTTP request object which triggered this print job
|
||||
"""
|
||||
return (
|
||||
self.render_to_pdf(label, item, request, **kwargs)
|
||||
self.render_to_pdf(label, item, **kwargs)
|
||||
.get_document() # type: ignore
|
||||
.write_pdf()
|
||||
)
|
||||
|
||||
def render_to_html(
|
||||
self, label: LabelTemplate, item: models.Model, request: Request, **kwargs
|
||||
) -> str:
|
||||
def render_to_html(self, label: LabelTemplate, item: models.Model, **kwargs) -> str:
|
||||
"""Helper method to render a label to HTML format for a specific item.
|
||||
|
||||
Arguments:
|
||||
label: The LabelTemplate object to render
|
||||
item: The item to render the label with
|
||||
request: The HTTP request object which triggered this print job
|
||||
"""
|
||||
html = self.machine_plugin.render_to_html(label, item, request, **kwargs)
|
||||
return html
|
||||
request = self._get_dummy_request()
|
||||
return self.machine_plugin.render_to_html(label, item, request, **kwargs)
|
||||
|
||||
def render_to_png(
|
||||
self, label: LabelTemplate, item: models.Model, request: Request, **kwargs
|
||||
) -> Image:
|
||||
self, label: LabelTemplate, item: models.Model, **kwargs
|
||||
) -> Union[Image, None]:
|
||||
"""Helper method to render a label to PNG format for a specific item.
|
||||
|
||||
Arguments:
|
||||
label: The LabelTemplate object to render
|
||||
item: The item to render the label with
|
||||
request: The HTTP request object which triggered this print job
|
||||
|
||||
Keyword Arguments:
|
||||
pdf_data (bytes): The pdf document as bytes (optional)
|
||||
@ -182,8 +173,20 @@ class LabelPrinterBaseDriver(BaseDriver):
|
||||
pdf2image_kwargs (dict): Additional keyword arguments to pass to the
|
||||
[`pdf2image.convert_from_bytes`](https://pdf2image.readthedocs.io/en/latest/reference.html#pdf2image.pdf2image.convert_from_bytes) method (optional)
|
||||
"""
|
||||
png = self.machine_plugin.render_to_png(label, item, request, **kwargs)
|
||||
return png
|
||||
request = self._get_dummy_request()
|
||||
return self.machine_plugin.render_to_png(label, item, request, **kwargs)
|
||||
|
||||
def _get_dummy_request(self):
|
||||
"""Return a dummy request object to it work with legacy code.
|
||||
|
||||
Note: this is a private method and can be removed at anytime
|
||||
"""
|
||||
r = HttpRequest()
|
||||
r.META['SERVER_PORT'] = '80'
|
||||
r.META['SERVER_NAME'] = 'localhost'
|
||||
r.user = AnonymousUser()
|
||||
|
||||
return r
|
||||
|
||||
required_overrides = [[print_label, print_labels]]
|
||||
|
||||
@ -229,6 +232,7 @@ class LabelPrinterStatus(MachineStatus):
|
||||
UNKNOWN = 101, _('Unknown'), 'secondary'
|
||||
PRINTING = 110, _('Printing'), 'primary'
|
||||
NO_MEDIA = 301, _('No media'), 'warning'
|
||||
PAPER_JAM = 302, _('Paper jam'), 'warning'
|
||||
DISCONNECTED = 400, _('Disconnected'), 'danger'
|
||||
|
||||
|
||||
|
@ -1,15 +1,20 @@
|
||||
"""Machine registry."""
|
||||
|
||||
import logging
|
||||
from typing import Union
|
||||
from typing import Union, cast
|
||||
from uuid import UUID
|
||||
|
||||
from django.core.cache import cache
|
||||
|
||||
from InvenTree.helpers_mixin import get_shared_class_instance_state_mixin
|
||||
from machine.machine_type import BaseDriver, BaseMachineType
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
|
||||
|
||||
class MachineRegistry:
|
||||
class MachineRegistry(
|
||||
get_shared_class_instance_state_mixin(lambda _x: f'machine:registry')
|
||||
):
|
||||
"""Machine registry class."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
@ -23,17 +28,27 @@ class MachineRegistry:
|
||||
self.machines: dict[str, BaseMachineType] = {}
|
||||
|
||||
self.base_drivers: list[type[BaseDriver]] = []
|
||||
self.errors: list[Union[str, Exception]] = []
|
||||
|
||||
self._hash = None
|
||||
|
||||
@property
|
||||
def errors(self) -> list[Union[str, Exception]]:
|
||||
"""List of registry errors."""
|
||||
return cast(list[Union[str, Exception]], self.get_shared_state('errors', []))
|
||||
|
||||
def handle_error(self, error: Union[Exception, str]):
|
||||
"""Helper function for capturing errors with the machine registry."""
|
||||
self.errors.append(error)
|
||||
self.set_shared_state('errors', self.errors + [error])
|
||||
|
||||
def initialize(self):
|
||||
def initialize(self, main: bool = False):
|
||||
"""Initialize the machine registry."""
|
||||
# clear cache for machines (only needed for global redis cache)
|
||||
if main and hasattr(cache, 'delete_pattern'): # pragma: no cover
|
||||
cache.delete_pattern(f'machine:*')
|
||||
|
||||
self.discover_machine_types()
|
||||
self.discover_drivers()
|
||||
self.load_machines()
|
||||
self.load_machines(main=main)
|
||||
|
||||
def discover_machine_types(self):
|
||||
"""Discovers all machine types by inferring all classes that inherit the BaseMachineType class."""
|
||||
@ -113,26 +128,39 @@ class MachineRegistry:
|
||||
|
||||
return self.driver_instances.get(slug, None)
|
||||
|
||||
def load_machines(self):
|
||||
def load_machines(self, main: bool = False):
|
||||
"""Load all machines defined in the database into the machine registry."""
|
||||
# Imports need to be in this level to prevent early db model imports
|
||||
from machine.models import MachineConfig
|
||||
|
||||
for machine_config in MachineConfig.objects.all():
|
||||
self.add_machine(machine_config, initialize=False)
|
||||
self.add_machine(
|
||||
machine_config, initialize=False, update_registry_hash=False
|
||||
)
|
||||
|
||||
# initialize drivers
|
||||
for driver in self.driver_instances.values():
|
||||
driver.init_driver()
|
||||
# initialize machines only in main thread
|
||||
if main:
|
||||
# initialize drivers
|
||||
for driver in self.driver_instances.values():
|
||||
driver.init_driver()
|
||||
|
||||
# initialize machines after all machine instances were created
|
||||
for machine in self.machines.values():
|
||||
if machine.active:
|
||||
machine.initialize()
|
||||
# initialize machines after all machine instances were created
|
||||
for machine in self.machines.values():
|
||||
if machine.active:
|
||||
machine.initialize()
|
||||
|
||||
logger.info('Initialized %s machines', len(self.machines.keys()))
|
||||
self._update_registry_hash()
|
||||
logger.info('Initialized %s machines', len(self.machines.keys()))
|
||||
else:
|
||||
self._hash = None # reset hash to force reload hash
|
||||
logger.info('Loaded %s machines', len(self.machines.keys()))
|
||||
|
||||
def add_machine(self, machine_config, initialize=True):
|
||||
def reload_machines(self):
|
||||
"""Reload all machines from the database."""
|
||||
self.machines = {}
|
||||
self.load_machines()
|
||||
|
||||
def add_machine(self, machine_config, initialize=True, update_registry_hash=True):
|
||||
"""Add a machine to the machine registry."""
|
||||
machine_type = self.machine_types.get(machine_config.machine_type, None)
|
||||
if machine_type is None:
|
||||
@ -145,11 +173,19 @@ class MachineRegistry:
|
||||
if initialize and machine.active:
|
||||
machine.initialize()
|
||||
|
||||
def update_machine(self, old_machine_state, machine_config):
|
||||
if update_registry_hash:
|
||||
self._update_registry_hash()
|
||||
|
||||
def update_machine(
|
||||
self, old_machine_state, machine_config, update_registry_hash=True
|
||||
):
|
||||
"""Notify the machine about an update."""
|
||||
if machine := machine_config.machine:
|
||||
machine.update(old_machine_state)
|
||||
|
||||
if update_registry_hash:
|
||||
self._update_registry_hash()
|
||||
|
||||
def restart_machine(self, machine):
|
||||
"""Restart a machine."""
|
||||
machine.restart()
|
||||
@ -157,6 +193,7 @@ class MachineRegistry:
|
||||
def remove_machine(self, machine: BaseMachineType):
|
||||
"""Remove a machine from the registry."""
|
||||
self.machines.pop(str(machine.pk), None)
|
||||
self._update_registry_hash()
|
||||
|
||||
def get_machines(self, **kwargs):
|
||||
"""Get loaded machines from registry (By default only initialized machines).
|
||||
@ -169,6 +206,8 @@ class MachineRegistry:
|
||||
active: (bool)
|
||||
base_driver: base driver (class)
|
||||
"""
|
||||
self._check_reload()
|
||||
|
||||
allowed_fields = [
|
||||
'name',
|
||||
'machine_type',
|
||||
@ -212,6 +251,7 @@ class MachineRegistry:
|
||||
|
||||
def get_machine(self, pk: Union[str, UUID]):
|
||||
"""Get machine from registry by pk."""
|
||||
self._check_reload()
|
||||
return self.machines.get(str(pk), None)
|
||||
|
||||
def get_drivers(self, machine_type: str):
|
||||
@ -222,5 +262,37 @@ class MachineRegistry:
|
||||
if driver.machine_type == machine_type
|
||||
]
|
||||
|
||||
def _calculate_registry_hash(self):
|
||||
"""Calculate a hash of the machine registry state."""
|
||||
from hashlib import md5
|
||||
|
||||
data = md5()
|
||||
|
||||
for pk, machine in self.machines.items():
|
||||
data.update(str(pk).encode())
|
||||
try:
|
||||
data.update(str(machine.machine_config.active).encode())
|
||||
except:
|
||||
# machine does not exist anymore, hash will be different
|
||||
pass
|
||||
|
||||
return str(data.hexdigest())
|
||||
|
||||
def _check_reload(self):
|
||||
"""Check if the registry needs to be reloaded, and reload it."""
|
||||
if not self._hash:
|
||||
self._hash = self._calculate_registry_hash()
|
||||
|
||||
last_hash = self.get_shared_state('hash', None)
|
||||
|
||||
if last_hash and last_hash != self._hash:
|
||||
logger.info('Machine registry has changed - reloading machines')
|
||||
self.reload_machines()
|
||||
|
||||
def _update_registry_hash(self):
|
||||
"""Save the current registry hash."""
|
||||
self._hash = self._calculate_registry_hash()
|
||||
self.set_shared_state('hash', self._hash)
|
||||
|
||||
|
||||
registry: MachineRegistry = MachineRegistry()
|
||||
|
@ -32,7 +32,7 @@ class TestMachineRegistryMixin(TestCase):
|
||||
registry.driver_instances = {}
|
||||
registry.machines = {}
|
||||
registry.base_drivers = []
|
||||
registry.errors = []
|
||||
registry.set_shared_state('errors', [])
|
||||
|
||||
return super().tearDown()
|
||||
|
||||
@ -111,7 +111,7 @@ class TestDriverMachineInterface(TestMachineRegistryMixin, TestCase):
|
||||
self.machines = [self.machine1, self.machine2, self.machine3]
|
||||
|
||||
# init registry
|
||||
registry.initialize()
|
||||
registry.initialize(main=True)
|
||||
|
||||
# mock machine implementation
|
||||
self.machine_mocks = {
|
||||
@ -230,7 +230,7 @@ class TestLabelPrinterMachineType(TestMachineRegistryMixin, InvenTreeAPITestCase
|
||||
active=True,
|
||||
)
|
||||
|
||||
registry.initialize()
|
||||
registry.initialize(main=True)
|
||||
driver_instance = cast(
|
||||
TestingLabelPrinterDriver,
|
||||
registry.get_driver_instance('testing-label-printer'),
|
||||
|
@ -93,11 +93,9 @@ class InvenTreeLabelPlugin(LabelPrintingMixin, InvenTreePlugin):
|
||||
|
||||
# execute the print job
|
||||
if driver.USE_BACKGROUND_WORKER is False:
|
||||
return driver.print_labels(machine, label, items, request, **print_kwargs)
|
||||
return driver.print_labels(machine, label, items, **print_kwargs)
|
||||
|
||||
offload_task(
|
||||
driver.print_labels, machine, label, items, request, **print_kwargs
|
||||
)
|
||||
offload_task(driver.print_labels, machine, label, items, **print_kwargs)
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
|
Reference in New Issue
Block a user