Skip to content

Commit

Permalink
Add algorithms
Browse files Browse the repository at this point in the history
  • Loading branch information
fqqb committed Apr 7, 2024
1 parent d83c8d8 commit 72612c0
Show file tree
Hide file tree
Showing 5 changed files with 328 additions and 42 deletions.
168 changes: 159 additions & 9 deletions src/yamcs/pymdb/algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,85 @@
from yamcs.pymdb.ancillary import AncillaryData

if TYPE_CHECKING:
from yamcs.pymdb.containers import Container
from yamcs.pymdb.parameters import Parameter
from yamcs.pymdb.systems import System


class Algorithm:
def __init__(
self,
system: System,
name: str,
*,
aliases: Mapping[str, str] | None = None,
short_description: str | None = None,
long_description: str | None = None,
extra: Mapping[str, str] | None = None,
language: str,
text: str,
inputs: Sequence[InputParameter] | None = None,
outputs: Sequence[OutputParameter] | None = None,
triggers: Sequence[Trigger] | None = None,
):
self.name: str = name
"""Short name of this algorithm"""

self.system: System = system
"""System this algorithm belongs to"""

self.aliases: dict[str, str] = dict(aliases or {})
"""Alternative names, keyed by namespace"""

self.short_description: str | None = short_description
"""Oneline description"""

self.long_description: str | None = long_description
"""Multiline description"""

self.extra: dict[str, str] = dict(extra or {})
"""Arbitrary information, keyed by name"""

self.language: str = language
"""Algorithm language"""

self.text: str = text
"""Algorithm text"""

self.inputs: list[InputParameter] = list(inputs or [])
"""Parameter inputs available to the algorithm"""

self.outputs: list[OutputParameter] = list(outputs or [])
"""Parameter outputs available to the algorithm"""

self.triggers: list[Trigger] = list(triggers or [])
"""Algorithm triggers"""

if name in system._algorithms_by_name:
raise Exception(f"System already contains an algorithm {name}")
system._algorithms_by_name[name] = self

@property
def qualified_name(self) -> str:
"""
Absolute path of this item covering the full system tree. For example,
an item ``C`` in a subystem ``B`` of a top-level system ``A`` is
represented as ``/A/B/C``
"""
path = "/" + self.name

parent = self.system
while parent:
path = "/" + parent.name + path
parent = getattr(parent, "system", None)

return path

def __lt__(self, other: Algorithm) -> bool:
return self.qualified_name < other.qualified_name

def __str__(self) -> str:
return self.qualified_name


class InputParameter:
Expand All @@ -15,26 +93,66 @@ def __init__(
parameter: Parameter | str,
*,
name: str | None = None,
required: bool = False,
):
self.parameter = parameter
"""Reference parameter"""

self.name: str | None = name
"""
Name to be used inside the algorithm. If not specified, a name
is derived from the basename of the parameter.
Variable name to be used inside the algorithm. If not specified, a
name is derived from the basename of the parameter.
"""

self.required: bool = required


class JavaAlgorithm:
class OutputParameter:
def __init__(
self,
java: str,
parameter: Parameter | str,
*,
name: str | None = None,
):
self.parameter = parameter
"""Reference parameter"""

self.name: str | None = name
"""
Variable name to be used inside the algorithm. If not specified, a
name is derived from the basename of the parameter.
"""


class Trigger:
pass


class ParameterTrigger(Trigger):

def __init__(self, parameter: Parameter | str) -> None:
self.parameter = parameter
"""Reference parameter"""


class ContainerTrigger(Trigger):

def __init__(self, container: Container | str) -> None:
self.container = container
"""Reference container"""


class UnnamedAlgorithm:
def __init__(
self,
language: str,
text: str,
*,
inputs: Sequence[InputParameter] | None = None,
extra: Mapping[str, str] | AncillaryData | None = None,
):
self.java: str = java
self.language: str = language
self.text: str = text

self.inputs: list[InputParameter] = list(inputs or [])
"""Parameter inputs available to the algorithm"""
Expand All @@ -47,7 +165,39 @@ def __init__(
self.extra = AncillaryData(extra)


hex_string_decoder = JavaAlgorithm("org.yamcs.algo.HexStringDecoder")
class UnnamedJavaAlgorithm(UnnamedAlgorithm):
def __init__(
self,
java: str,
*,
inputs: Sequence[InputParameter] | None = None,
extra: Mapping[str, str] | AncillaryData | None = None,
):
super().__init__(
language="Java",
text=java,
inputs=inputs,
extra=extra,
)


class UnnamedJavaScriptAlgorithm(UnnamedAlgorithm):
def __init__(
self,
js: str,
*,
inputs: Sequence[InputParameter] | None = None,
extra: Mapping[str, str] | AncillaryData | None = None,
):
super().__init__(
language="JavaScript",
text=js,
inputs=inputs,
extra=extra,
)


hex_string_decoder = UnnamedJavaAlgorithm("org.yamcs.algo.HexStringDecoder")
"""
Decoder algorithm that returns the `string` value in hex format of read bytes.
This is intended to be used for special use cases where the hex value
Expand All @@ -59,7 +209,7 @@ def __init__(
The implementation assumes a fixed-size encoding.
"""

remaining_binary_decoder = JavaAlgorithm("org.yamcs.algo.RemainingBinaryDecoder")
remaining_binary_decoder = UnnamedJavaAlgorithm("org.yamcs.algo.RemainingBinaryDecoder")
"""
A decoder that returns a binary value containing all of the remaining bytes.
Expand All @@ -68,15 +218,15 @@ def __init__(
"""


reverse_binary_decoder = JavaAlgorithm("org.yamcs.algo.ReverseBinaryDecoder")
reverse_binary_decoder = UnnamedJavaAlgorithm("org.yamcs.algo.ReverseBinaryDecoder")
"""
A decoder that returns a binary value containing all of the remaining bytes.
An example where this may be useful is a packet that contains an arbitrarily
sized blob of data with no length indication.
"""

reverse_binary_encoder = JavaAlgorithm("org.yamcs.algo.ReverseBinaryEncoder")
reverse_binary_encoder = UnnamedJavaAlgorithm("org.yamcs.algo.ReverseBinaryEncoder")
"""
A custom data encoder that converts provided binary to encoded binary in the
reverse byte order.
Expand Down
15 changes: 9 additions & 6 deletions src/yamcs/pymdb/encodings.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from __future__ import annotations

from enum import Enum, auto
from typing import TypeAlias
from typing import TYPE_CHECKING, TypeAlias

from yamcs.pymdb.algorithms import JavaAlgorithm
if TYPE_CHECKING:
from yamcs.pymdb.algorithms import UnnamedAlgorithm


class Charset(Enum):
Expand Down Expand Up @@ -74,8 +77,8 @@ def __init__(
self,
bits: int | None = None,
length_bits: int | None = None,
encoder: JavaAlgorithm | None = None,
decoder: JavaAlgorithm | None = None,
encoder: UnnamedAlgorithm | None = None,
decoder: UnnamedAlgorithm | None = None,
) -> None:
super().__init__(bits=bits)

Expand All @@ -84,12 +87,12 @@ def __init__(
Length in bits of a leading size tag
"""

self.encoder: JavaAlgorithm | None = encoder
self.encoder: UnnamedAlgorithm | None = encoder
"""
Custom encoder, when this encoding is used for telecommanding
"""

self.decoder: JavaAlgorithm | None = decoder
self.decoder: UnnamedAlgorithm | None = decoder
"""
Custom decoder, when this encoding is used for telemetry
"""
Expand Down
40 changes: 37 additions & 3 deletions src/yamcs/pymdb/systems.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from __future__ import annotations

from collections.abc import Mapping
from typing import TYPE_CHECKING

from yamcs.pymdb import xtce
from yamcs.pymdb.commands import Command
from yamcs.pymdb.containers import Container
from yamcs.pymdb.parameters import Parameter

if TYPE_CHECKING:
from yamcs.pymdb.algorithms import Algorithm
from yamcs.pymdb.commands import Command
from yamcs.pymdb.containers import Container
from yamcs.pymdb.parameters import Parameter


class System:
Expand Down Expand Up @@ -44,6 +48,7 @@ def __init__(
self.extra: dict[str, str] = dict(extra or {})
"""Arbitrary information, keyed by name"""

self._algorithms_by_name: dict[str, Algorithm] = {}
self._commands_by_name: dict[str, Command] = {}
self._containers_by_name: dict[str, Container] = {}
self._parameters_by_name: dict[str, Parameter] = {}
Expand Down Expand Up @@ -87,6 +92,15 @@ def parameters(self) -> list[Parameter]:
copy.sort()
return copy

@property
def algorithms(self) -> list[Algorithm]:
"""
Algorithms directly belonging to this system
"""
copy = list(self._algorithms_by_name.values())
copy.sort()
return copy

@property
def subsystems(self) -> list[Subsystem]:
"""
Expand Down Expand Up @@ -132,6 +146,18 @@ def remove_container(self, name: str) -> bool:
except KeyError:
return False

def remove_algorithm(self, name: str) -> bool:
"""
Removes an algorithm directly belonging to this system.
Raises an exception if no such algorithm exists
"""
try:
self._algorithms_by_name.pop(name)
return True
except KeyError:
return False

def remove_subsystem(self, name: str) -> bool:
"""
Removes a subsystem directly belonging to this system.
Expand Down Expand Up @@ -168,6 +194,14 @@ def find_container(self, name: str) -> Container:
"""
return self._containers_by_name[name]

def find_algorithm(self, name: str) -> Algorithm:
"""
Find an algorithm belonging directly to this system.
Raises an exception if no algorithm is found
"""
return self._algorithms_by_name[name]

def find_subsystem(self, name: str) -> Subsystem:
"""
Find a subsystem belonging directly to this system.
Expand Down
4 changes: 2 additions & 2 deletions src/yamcs/pymdb/verifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from typing import TYPE_CHECKING, TypeAlias

if TYPE_CHECKING:
from yamcs.pymdb.algorithms import JavaAlgorithm
from yamcs.pymdb.algorithms import UnnamedAlgorithm
from yamcs.pymdb.containers import Container
from yamcs.pymdb.expressions import Expression
from yamcs.pymdb.parameters import Parameter
Expand All @@ -17,7 +17,7 @@ class TerminationAction(Enum):


class AlgorithmCheck:
def __init__(self, algorithm: JavaAlgorithm):
def __init__(self, algorithm: UnnamedAlgorithm):
self.algorithm = algorithm


Expand Down
Loading

0 comments on commit 72612c0

Please sign in to comment.