Updated tests and added core live value handling
Removed useless tests, will need to complete a more comprehensive test suite however this is not the priority right now Added a live value dict in the core whose values are populated by the core's input thread
This commit is contained in:
@@ -5,9 +5,10 @@
|
||||
import logging
|
||||
from queue import Empty, Queue
|
||||
import threading
|
||||
from threading import Thread
|
||||
from threading import Lock, Thread
|
||||
from time import monotonic, monotonic_ns, sleep
|
||||
from typing import Literal
|
||||
from uuid import UUID
|
||||
|
||||
|
||||
from dynalab_core.config import CoreConfig
|
||||
@@ -18,6 +19,7 @@ from dynalab_core.protocols.common import VersionDescriptor
|
||||
from dynalab_core.protocols.json.server import JsonServer
|
||||
from dynalab_core.protocols.packets import ProtocolMessage
|
||||
from dynalab_core.protocols.packets.data import ValueDescriptor
|
||||
from dynalab_core.values import Value
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -32,6 +34,9 @@ class Core:
|
||||
self._core_version: VersionDescriptor = CORE_VERSION
|
||||
self._stop_event: threading.Event = threading.Event()
|
||||
self._core_config: CoreConfig = config
|
||||
# TODO: Implement live values dict
|
||||
self._live_values: dict[UUID, Value] = {}
|
||||
self._live_values_lock = Lock()
|
||||
|
||||
self._data_input_queue: Queue[ProtocolMessage] = Queue(524288)
|
||||
self._input_worker_thread = Thread(
|
||||
@@ -98,21 +103,19 @@ class Core:
|
||||
)
|
||||
|
||||
def _input_worker(self) -> None:
|
||||
last_log = 0
|
||||
while not self._stop_event.is_set():
|
||||
now = monotonic_ns()
|
||||
messages: list[ProtocolMessage] = []
|
||||
try:
|
||||
while not self._data_input_queue.empty():
|
||||
messages.append(self._data_input_queue.get_nowait())
|
||||
message = self._data_input_queue.get_nowait()
|
||||
except Empty:
|
||||
self._stop_event.wait(0.01)
|
||||
continue
|
||||
else:
|
||||
for message in messages:
|
||||
if isinstance(message, ValueDescriptor):
|
||||
pass
|
||||
|
||||
if now > last_log + 2000 * 1_000_000:
|
||||
log.debug(f"Core queue size: {self._data_input_queue.qsize()}")
|
||||
last_log = now
|
||||
if isinstance(message, ValueDescriptor):
|
||||
with self._live_values_lock:
|
||||
live_value = self._live_values.get(message.signal_id)
|
||||
|
||||
if live_value is None:
|
||||
live_value = Value()
|
||||
self._live_values[message.signal_id] = live_value
|
||||
|
||||
live_value.update(message.value)
|
||||
|
||||
@@ -6,7 +6,7 @@ import logging
|
||||
from queue import Empty, Full, Queue
|
||||
import threading
|
||||
from threading import RLock, Thread
|
||||
from time import monotonic, sleep
|
||||
from time import monotonic, monotonic_ns, sleep
|
||||
from uuid import UUID
|
||||
|
||||
from dynalab_core.constants import HELLO_PACKET
|
||||
@@ -15,7 +15,7 @@ from dynalab_core.protocols.errors import (
|
||||
ConnectorRegistryAlreadyRegisteredError,
|
||||
)
|
||||
from dynalab_core.protocols.packets import ProtocolMessage
|
||||
from dynalab_core.protocols.packets.data import ValueDescriptor
|
||||
from dynalab_core.protocols.packets.data import ValueBatch, ValueDescriptor
|
||||
from dynalab_core.protocols.packets.handshake import (
|
||||
ConnectorHello,
|
||||
HandshakeAccepted,
|
||||
@@ -208,20 +208,34 @@ class ConnectorEndpoint:
|
||||
"thread_name": self._input_worker_thread.name,
|
||||
},
|
||||
)
|
||||
last_log = 0
|
||||
while not self._stop_event.is_set():
|
||||
now = monotonic_ns()
|
||||
messages: list[ProtocolMessage] = []
|
||||
try:
|
||||
message = self._get_ingress_packet_no_wait()
|
||||
while not self._packet_ingress_queue.empty():
|
||||
messages.append(self._get_ingress_packet_no_wait())
|
||||
except Empty:
|
||||
self._stop_event.wait(0.01)
|
||||
continue
|
||||
else:
|
||||
if isinstance(message, Heartbeat):
|
||||
self._heartbeat_ingress_queue.put_nowait(message)
|
||||
elif isinstance(message, ValueDescriptor):
|
||||
if message.signal_id in (
|
||||
signal.id for signal in self._connector_hello.signals
|
||||
):
|
||||
self._core_input_queue.put_nowait(message)
|
||||
for message in messages:
|
||||
if isinstance(message, Heartbeat):
|
||||
self._heartbeat_ingress_queue.put_nowait(message)
|
||||
elif isinstance(message, ValueDescriptor):
|
||||
if message.signal_id in (
|
||||
signal.id for signal in self._connector_hello.signals
|
||||
):
|
||||
self._core_input_queue.put_nowait(message)
|
||||
elif isinstance(message, ValueBatch):
|
||||
for value in message.values:
|
||||
if value.signal_id in (
|
||||
signal.id for signal in self._connector_hello.signals
|
||||
):
|
||||
self._core_input_queue.put_nowait(value)
|
||||
|
||||
if now > last_log + 2000 * 1_000_000:
|
||||
log.debug(f"Endpoint queue size: {self._packet_ingress_queue.qsize()}")
|
||||
last_log = now
|
||||
self._input_worker_stopped_event.set()
|
||||
log.debug(
|
||||
"Connector endpoint input worker %s stopped",
|
||||
|
||||
@@ -6,9 +6,10 @@ import asyncio
|
||||
from asyncio import Server
|
||||
import logging
|
||||
from queue import Empty
|
||||
from statistics import mean
|
||||
import threading
|
||||
from threading import Thread
|
||||
from time import monotonic
|
||||
from time import monotonic, monotonic_ns
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
@@ -163,9 +164,19 @@ class JsonServer:
|
||||
endpoint: ConnectorEndpoint,
|
||||
) -> None:
|
||||
log.debug(f"Started input task for {endpoint.uuid()}")
|
||||
intervals: list[int] = []
|
||||
last_val: int = 0
|
||||
last_print: int = 0
|
||||
while not self._stop_event.is_set():
|
||||
message = await read_message(reader)
|
||||
endpoint.put_ingress_packet(message)
|
||||
now = monotonic_ns()
|
||||
intervals.append(now - last_val)
|
||||
last_val = now
|
||||
if now > last_print + 2000 * 1_000_000:
|
||||
log.debug(f"Server input frequency: {1_000_000 / mean(intervals)}kHz")
|
||||
last_print = now
|
||||
intervals = []
|
||||
|
||||
async def _output_task(
|
||||
self,
|
||||
|
||||
@@ -6,7 +6,7 @@ from typing import Annotated
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from dynalab_core.protocols.packets.data import ValueDescriptor
|
||||
from dynalab_core.protocols.packets.data import ValueBatch, ValueDescriptor
|
||||
from dynalab_core.protocols.packets.handshake import (
|
||||
ConnectorHello,
|
||||
DynaLabHello,
|
||||
@@ -22,6 +22,7 @@ ProtocolMessage = Annotated[
|
||||
| HandshakeAccepted
|
||||
| HandshakeRejected
|
||||
| Heartbeat
|
||||
| ValueDescriptor,
|
||||
| ValueDescriptor
|
||||
| ValueBatch,
|
||||
Field(discriminator="type"),
|
||||
]
|
||||
|
||||
@@ -11,3 +11,8 @@ class ValueDescriptor(BaseModel):
|
||||
type: Literal["value_descriptor"] = "value_descriptor"
|
||||
signal_id: UUID
|
||||
value: float
|
||||
|
||||
|
||||
class ValueBatch(BaseModel):
|
||||
type: Literal["value_batch"] = "value_batch"
|
||||
values: list[ValueDescriptor]
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# 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 threading import Lock
|
||||
|
||||
|
||||
class Value:
|
||||
_value: float = 0
|
||||
_last_updated: int = 0
|
||||
_lock: Lock = Lock()
|
||||
|
||||
def update(self, value: float) -> None:
|
||||
with self._lock:
|
||||
self._value = value
|
||||
|
||||
def get(self) -> float:
|
||||
with self._lock:
|
||||
return self._value
|
||||
Reference in New Issue
Block a user