Skip to content

Commit

Permalink
Merge pull request #306 from andlaus/refactor_compumethods
Browse files Browse the repository at this point in the history
Refactor the compu methods
  • Loading branch information
andlaus authored May 23, 2024
2 parents d8dcda1 + 7cf8993 commit b847bc8
Show file tree
Hide file tree
Showing 30 changed files with 1,648 additions and 973 deletions.
Binary file modified examples/somersault.pdx
Binary file not shown.
86 changes: 47 additions & 39 deletions examples/somersaultecu.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
from odxtools.companyspecificinfo import CompanySpecificInfo
from odxtools.comparaminstance import ComparamInstance
from odxtools.comparamsubset import ComparamSubset
from odxtools.compumethods.compumethod import CompuMethod
from odxtools.compumethods.compuconst import CompuConst
from odxtools.compumethods.compuinternaltophys import CompuInternalToPhys
from odxtools.compumethods.compumethod import CompuCategory, CompuMethod
from odxtools.compumethods.compuscale import CompuScale
from odxtools.compumethods.identicalcompumethod import IdenticalCompuMethod
from odxtools.compumethods.limit import Limit
Expand Down Expand Up @@ -412,49 +414,55 @@ class SomersaultSID(IntEnum):
# computation methods
somersault_compumethods: Dict[str, CompuMethod] = {
"uint_passthrough":
IdenticalCompuMethod(internal_type=DataType.A_UINT32, physical_type=DataType.A_UINT32),
IdenticalCompuMethod(
category=CompuCategory.IDENTICAL,
compu_internal_to_phys=None,
compu_phys_to_internal=None,
internal_type=DataType.A_UINT32,
physical_type=DataType.A_UINT32),
"float_passthrough":
IdenticalCompuMethod(internal_type=DataType.A_FLOAT32, physical_type=DataType.A_FLOAT32),
IdenticalCompuMethod(
category=CompuCategory.IDENTICAL,
compu_internal_to_phys=None,
compu_phys_to_internal=None,
internal_type=DataType.A_FLOAT32,
physical_type=DataType.A_FLOAT32),
"boolean":
TexttableCompuMethod(
category=CompuCategory.TEXTTABLE,
compu_phys_to_internal=None,
compu_internal_to_phys=CompuInternalToPhys(
compu_default_value=None,
prog_code=None,
compu_scales=[
CompuScale(
compu_const=CompuConst(v=None, vt="false", data_type=DataType.A_UTF8STRING),
lower_limit=Limit(
value_raw="0", value_type=DataType.A_UINT32, interval_type=None),
upper_limit=Limit(
value_raw="0", value_type=DataType.A_UINT32, interval_type=None),
short_label=None,
description=None,
internal_type=DataType.A_UINT32,
physical_type=DataType.A_UNICODE2STRING,
compu_inverse_value=None,
compu_rational_coeffs=None),
CompuScale(
compu_const=CompuConst(v=None, vt="true", data_type=DataType.A_UTF8STRING),
lower_limit=Limit(
value_raw="1", value_type=DataType.A_UINT32, interval_type=None),
upper_limit=Limit(
value_raw="1", value_type=DataType.A_UINT32, interval_type=None),
short_label=None,
description=None,
internal_type=DataType.A_UINT32,
physical_type=DataType.A_UNICODE2STRING,
compu_inverse_value=None,
compu_rational_coeffs=None),
],
),
internal_type=DataType.A_UINT32,
physical_type=DataType.A_UNICODE2STRING,
compu_default_value=CompuScale(
compu_const="undefined",
lower_limit=None,
upper_limit=None,
short_label=None,
description=None,
internal_type=DataType.A_UINT32,
physical_type=DataType.A_UNICODE2STRING,
compu_inverse_value=None,
compu_rational_coeffs=None),
internal_to_phys=[
CompuScale(
compu_const="false",
lower_limit=Limit(
value_raw="0", value_type=DataType.A_UINT32, interval_type=None),
upper_limit=Limit(
value_raw="0", value_type=DataType.A_UINT32, interval_type=None),
short_label=None,
description=None,
internal_type=DataType.A_UINT32,
physical_type=DataType.A_UNICODE2STRING,
compu_inverse_value=None,
compu_rational_coeffs=None),
CompuScale(
compu_const="true",
lower_limit=Limit(
value_raw="1", value_type=DataType.A_UINT32, interval_type=None),
upper_limit=Limit(
value_raw="1", value_type=DataType.A_UINT32, interval_type=None),
short_label=None,
description=None,
internal_type=DataType.A_UINT32,
physical_type=DataType.A_UNICODE2STRING,
compu_inverse_value=None,
compu_rational_coeffs=None),
],
),
}

Expand Down
31 changes: 31 additions & 0 deletions odxtools/compumethods/compuconst.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# SPDX-License-Identifier: MIT
from dataclasses import dataclass
from typing import Optional
from xml.etree import ElementTree

from ..odxtypes import AtomicOdxType, DataType


@dataclass
class CompuConst:
v: Optional[str]
vt: Optional[str]

data_type: DataType

@staticmethod
def compuvalue_from_et(et_element: ElementTree.Element, *, data_type: DataType) -> "CompuConst":

v = et_element.findtext("V")
vt = et_element.findtext("VT")

return CompuConst(v=v, vt=vt, data_type=data_type)

def __post_init__(self) -> None:
self._value: Optional[AtomicOdxType] = self.vt
if self.v is not None:
self._value = self.data_type.from_string(self.v)

@property
def value(self) -> Optional[AtomicOdxType]:
return self._value
27 changes: 27 additions & 0 deletions odxtools/compumethods/compudefaultvalue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# SPDX-License-Identifier: MIT
from dataclasses import dataclass
from typing import Optional
from xml.etree import ElementTree

from ..odxtypes import DataType
from ..utils import dataclass_fields_asdict
from .compuconst import CompuConst
from .compuinversevalue import CompuInverseValue


@dataclass
class CompuDefaultValue(CompuConst):
compu_inverse_value: Optional[CompuInverseValue]

@staticmethod
def compuvalue_from_et(et_element: ElementTree.Element, *,
data_type: DataType) -> "CompuDefaultValue":
kwargs = dataclass_fields_asdict(
CompuConst.compuvalue_from_et(et_element, data_type=data_type))

compu_inverse_value = None
if (civ_elem := et_element.find("COMPU-INVERSE-VALUE")) is not None:
compu_inverse_value = CompuInverseValue.compuvalue_from_et(
civ_elem, data_type=data_type)

return CompuDefaultValue(**kwargs, compu_inverse_value=compu_inverse_value)
39 changes: 39 additions & 0 deletions odxtools/compumethods/compuinternaltophys.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# SPDX-License-Identifier: MIT
from dataclasses import dataclass
from typing import List, Optional
from xml.etree import ElementTree

from ..odxlink import OdxDocFragment
from ..odxtypes import DataType
from ..progcode import ProgCode
from .compudefaultvalue import CompuDefaultValue
from .compuscale import CompuScale


@dataclass
class CompuInternalToPhys:
compu_scales: List[CompuScale]
prog_code: Optional[ProgCode]
compu_default_value: Optional[CompuDefaultValue]

@staticmethod
def compu_internal_to_phys_from_et(et_element: ElementTree.Element,
doc_frags: List[OdxDocFragment], *, internal_type: DataType,
physical_type: DataType) -> "CompuInternalToPhys":
compu_scales = [
CompuScale.compuscale_from_et(
cse, doc_frags, internal_type=internal_type, physical_type=physical_type)
for cse in et_element.iterfind("COMPU-SCALES/COMPU-SCALE")
]

prog_code = None
if (pce := et_element.find("PROG-CODE")) is not None:
prog_code = ProgCode.from_et(pce, doc_frags)

compu_default_value = None
if (cdve := et_element.find("COMPU-DEFAULT-VALUE")) is not None:
compu_default_value = CompuDefaultValue.compuvalue_from_et(
cdve, data_type=physical_type)

return CompuInternalToPhys(
compu_scales=compu_scales, prog_code=prog_code, compu_default_value=compu_default_value)
7 changes: 7 additions & 0 deletions odxtools/compumethods/compuinversevalue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: MIT

from .compuconst import CompuConst

# make CompuInverseValue an alias for CompuConst. The XSD defines two
# separate but identical groups for this (why?)...
CompuInverseValue = CompuConst
79 changes: 67 additions & 12 deletions odxtools/compumethods/compumethod.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,81 @@
# SPDX-License-Identifier: MIT
from dataclasses import dataclass
from typing import Literal
from enum import Enum
from typing import List, Optional
from xml.etree import ElementTree

from ..exceptions import odxraise
from ..odxlink import OdxDocFragment
from ..odxtypes import AtomicOdxType, DataType
from .compuinternaltophys import CompuInternalToPhys
from .compuphystointernal import CompuPhysToInternal

CompuMethodCategory = Literal[
"IDENTICAL",
"LINEAR",
"SCALE-LINEAR",
"TAB-INTP",
"TEXTTABLE",
]

class CompuCategory(Enum):
IDENTICAL = "IDENTICAL"
LINEAR = "LINEAR"
SCALE_LINEAR = "SCALE-LINEAR"
TEXTTABLE = "TEXTTABLE"
COMPUCODE = "COMPUCODE"
TAB_INTP = "TAB-INTP"
RAT_FUNC = "RAT-FUNC"
SCALE_RAT_FUNC = "SCALE-RAT-FUNC"


@dataclass
class CompuMethod:
internal_type: DataType
"""A compu method translates between the internal representation
of a value and their physical representation.
There are many compu methods, but all of them are specified using
the same mechanism: The conversion from internal to physical
quantities is specified using the COMPU-INTERNAL-TO-PHYS subtag,
and the inverse is covered by
COMPU-PHYS-TO-INTERNAL. Alternatively to directly specifying the
parameters needed for conversion, it is also possible to specify a
Java program which does the conversion (doing this excludes using
ODX in non-Java contexts, though).
For details, refer to ASAM specification MCD-2 D (ODX), section 7.3.6.6.
"""

category: CompuCategory
compu_internal_to_phys: Optional[CompuInternalToPhys]
compu_phys_to_internal: Optional[CompuPhysToInternal]

physical_type: DataType
internal_type: DataType

@property
def category(self) -> CompuMethodCategory:
raise NotImplementedError()
@staticmethod
def compu_method_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
internal_type: DataType, physical_type: DataType) -> "CompuMethod":
cat_text = et_element.findtext("CATEGORY")
if cat_text is None:
odxraise("No category specified for compu method")
cat_text = "IDENTICAL"

try:
category = CompuCategory(cat_text)
except ValueError:
odxraise(f"Encountered compu method of unknown category '{cat_text}'")
category = CompuCategory.IDENTICAL

compu_internal_to_phys = None
if (citp_elem := et_element.find("COMPU-INTERNAL-TO-PHYS")) is not None:
compu_internal_to_phys = CompuInternalToPhys.compu_internal_to_phys_from_et(
citp_elem, doc_frags, internal_type=internal_type, physical_type=physical_type)
compu_phys_to_internal = None
if (cpti_elem := et_element.find("COMPU-PHYS-TO-INTERNAL")) is not None:
compu_phys_to_internal = CompuPhysToInternal.compu_phys_to_internal_from_et(
cpti_elem, doc_frags, internal_type=internal_type, physical_type=physical_type)

return CompuMethod(
category=category,
compu_internal_to_phys=compu_internal_to_phys,
compu_phys_to_internal=compu_phys_to_internal,
physical_type=physical_type,
internal_type=internal_type)

def convert_physical_to_internal(self, physical_value: AtomicOdxType) -> AtomicOdxType:
raise NotImplementedError()
Expand Down
39 changes: 39 additions & 0 deletions odxtools/compumethods/compuphystointernal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# SPDX-License-Identifier: MIT
from dataclasses import dataclass
from typing import List, Optional
from xml.etree import ElementTree

from ..odxlink import OdxDocFragment
from ..odxtypes import DataType
from ..progcode import ProgCode
from .compudefaultvalue import CompuDefaultValue
from .compuscale import CompuScale


@dataclass
class CompuPhysToInternal:
compu_scales: List[CompuScale]
prog_code: Optional[ProgCode]
compu_default_value: Optional[CompuDefaultValue]

@staticmethod
def compu_phys_to_internal_from_et(et_element: ElementTree.Element,
doc_frags: List[OdxDocFragment], *, internal_type: DataType,
physical_type: DataType) -> "CompuPhysToInternal":
compu_scales = [
CompuScale.compuscale_from_et(
cse, doc_frags, internal_type=internal_type, physical_type=physical_type)
for cse in et_element.iterfind("COMPU-SCALES/COMPU-SCALE")
]

prog_code = None
if (pce := et_element.find("PROG-CODE")) is not None:
prog_code = ProgCode.from_et(pce, doc_frags)

compu_default_value = None
if (cdve := et_element.find("COMPU-DEFAULT-VALUE")) is not None:
compu_default_value = CompuDefaultValue.compuvalue_from_et(
cdve, data_type=internal_type)

return CompuPhysToInternal(
compu_scales=compu_scales, prog_code=prog_code, compu_default_value=compu_default_value)
Loading

0 comments on commit b847bc8

Please sign in to comment.