Initial DeriveUnit implementation

This contains the initial DeriveUnit implementation, including signature
validation, this initial implentation only supports ValueDescriptors in
the signature alongside the SignalDescriptor and returns a single
ValueDescriptor

The worker thread still needs to be defined and started but the offline
processing has already been validate to work as a first prototype
This commit is contained in:
2026-08-23 12:27:58 +01:00
parent ebe7e92e22
commit d9570cd153
4 changed files with 166 additions and 0 deletions
+107
View File
@@ -0,0 +1,107 @@
# Copyright (C) 2026 Hector van der Aa <hector@h3cx.dev>
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
from collections.abc import Callable
from enum import IntEnum
import inspect
from queue import Empty, Queue
from threading import Thread
import threading
from time import sleep
from typing import Literal
from uuid import UUID
from dynalab_core.errors import (
DeriveUnitArgsMismatchError,
DeriveUnitInvalidSignatureError,
)
from dynalab_core.protocols.packets.data import ValueDescriptor
from dynalab_core.protocols.packets.handshake import SignalDescriptor
class ParserState(IntEnum):
INIT = 0
VALUES_DONE = 1
SIGNAL_DONE = 2
class DeriveUnit:
def __init__(
self,
unit_uuid: UUID,
processing_function: Callable[..., ValueDescriptor],
input_signals: list[SignalDescriptor],
return_signal: SignalDescriptor,
) -> None:
self._id: UUID = unit_uuid
self._worker_thread: Thread | None = None
self._process_function: Callable = processing_function
self._return_signal: SignalDescriptor = return_signal
self._stop_event: threading.Event = threading.Event()
self._input_queue: Queue[list[ValueDescriptor]] = []
self._output_queue: Queue[ValueDescriptor] = []
self._num_input_args: int = 0
self._parser_state: int = ParserState.INIT
sig = inspect.signature(self._process_function)
for name, param in sig.parameters.items():
if self._parser_state == ParserState.INIT:
if param.annotation is ValueDescriptor:
self._num_input_args += 1
else:
self._parser_state = ParserState.VALUES_DONE
if self._parser_state == ParserState.VALUES_DONE:
if param.annotation is SignalDescriptor:
self._parser_state = ParserState.SIGNAL_DONE
continue
else:
raise DeriveUnitInvalidSignatureError
if self._parser_state == ParserState.SIGNAL_DONE:
raise DeriveUnitInvalidSignatureError
if sig.return_annotation is not ValueDescriptor:
raise DeriveUnitInvalidSignatureError
print(f"Signature validated with {self._num_input_args} input values")
# TODO: Define and start worker thread
def worker_function(self) -> None:
while not self._stop_event.is_set():
try:
input_args = self._input_queue.get(timeout=0.1)
except Empty:
continue
if len(input_args) != self._num_input_args:
continue
self._output_queue.put(
self._process_function(*input_args, self._return_signal)
)
def put_data(self, input: list[ValueDescriptor]) -> None:
self._input_queue.put(input)
def get_output(self) -> ValueDescriptor | None:
try:
return self._output_queue.get_nowait()
except Empty:
return None
def get_all_output(self) -> list[ValueDescriptor]:
output: list[ValueDescriptor] = []
while True:
try:
output.append(self._output_queue.get_nowait())
except Empty:
return output
def process_offline(self, input_args: list[ValueDescriptor]) -> ValueDescriptor:
if len(input_args) == self._num_input_args:
return self._process_function(*input_args, self._return_signal)
raise DeriveUnitArgsMismatchError