diff --git a/py/poetry.lock b/py/poetry.lock index f839a57..773b187 100644 --- a/py/poetry.lock +++ b/py/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "aiohttp" @@ -361,6 +361,29 @@ files = [ {file = "boltons-23.0.0.tar.gz", hash = "sha256:8c50a71829525835ca3c849c7ed2511610c972b4dddfcd41a4a5447222beb4b0"}, ] +[[package]] +name = "cattrs" +version = "23.2.3" +description = "Composable complex class support for attrs and dataclasses." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cattrs-23.2.3-py3-none-any.whl", hash = "sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108"}, + {file = "cattrs-23.2.3.tar.gz", hash = "sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f"}, +] + +[package.dependencies] +attrs = ">=23.1.0" + +[package.extras] +bson = ["pymongo (>=4.4.0)"] +cbor2 = ["cbor2 (>=5.4.6)"] +msgpack = ["msgpack (>=1.0.5)"] +orjson = ["orjson (>=3.9.2)"] +pyyaml = ["pyyaml (>=6.0)"] +tomlkit = ["tomlkit (>=0.11.8)"] +ujson = ["ujson (>=5.7.0)"] + [[package]] name = "certifi" version = "2023.7.22" @@ -576,6 +599,7 @@ files = [ {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18a64814ae7bce73925131381603fff0116e2df25230dfc80d6d690aa6e20b37"}, {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c81f22b4f572f8a2110b0b741bb64e5a6427e0a198b2cdc1fbaf85f352a3aa"}, {file = "contourpy-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53cc3a40635abedbec7f1bde60f8c189c49e84ac180c665f2cd7c162cc454baa"}, + {file = "contourpy-1.1.0-cp310-cp310-win32.whl", hash = "sha256:9b2dd2ca3ac561aceef4c7c13ba654aaa404cf885b187427760d7f7d4c57cff8"}, {file = "contourpy-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:1f795597073b09d631782e7245016a4323cf1cf0b4e06eef7ea6627e06a37ff2"}, {file = "contourpy-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0b7b04ed0961647691cfe5d82115dd072af7ce8846d31a5fac6c142dcce8b882"}, {file = "contourpy-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27bc79200c742f9746d7dd51a734ee326a292d77e7d94c8af6e08d1e6c15d545"}, @@ -584,6 +608,7 @@ files = [ {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5cec36c5090e75a9ac9dbd0ff4a8cf7cecd60f1b6dc23a374c7d980a1cd710e"}, {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f0cbd657e9bde94cd0e33aa7df94fb73c1ab7799378d3b3f902eb8eb2e04a3a"}, {file = "contourpy-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:181cbace49874f4358e2929aaf7ba84006acb76694102e88dd15af861996c16e"}, + {file = "contourpy-1.1.0-cp311-cp311-win32.whl", hash = "sha256:edb989d31065b1acef3828a3688f88b2abb799a7db891c9e282df5ec7e46221b"}, {file = "contourpy-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb3b7d9e6243bfa1efb93ccfe64ec610d85cfe5aec2c25f97fbbd2e58b531256"}, {file = "contourpy-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bcb41692aa09aeb19c7c213411854402f29f6613845ad2453d30bf421fe68fed"}, {file = "contourpy-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5d123a5bc63cd34c27ff9c7ac1cd978909e9c71da12e05be0231c608048bb2ae"}, @@ -592,6 +617,7 @@ files = [ {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:317267d915490d1e84577924bd61ba71bf8681a30e0d6c545f577363157e5e94"}, {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d551f3a442655f3dcc1285723f9acd646ca5858834efeab4598d706206b09c9f"}, {file = "contourpy-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7a117ce7df5a938fe035cad481b0189049e8d92433b4b33aa7fc609344aafa1"}, + {file = "contourpy-1.1.0-cp38-cp38-win32.whl", hash = "sha256:108dfb5b3e731046a96c60bdc46a1a0ebee0760418951abecbe0fc07b5b93b27"}, {file = "contourpy-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4f26b25b4f86087e7d75e63212756c38546e70f2a92d2be44f80114826e1cd4"}, {file = "contourpy-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc00bb4225d57bff7ebb634646c0ee2a1298402ec10a5fe7af79df9a51c1bfd9"}, {file = "contourpy-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:189ceb1525eb0655ab8487a9a9c41f42a73ba52d6789754788d1883fb06b2d8a"}, @@ -600,6 +626,7 @@ files = [ {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:143dde50520a9f90e4a2703f367cf8ec96a73042b72e68fcd184e1279962eb6f"}, {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e94bef2580e25b5fdb183bf98a2faa2adc5b638736b2c0a4da98691da641316a"}, {file = "contourpy-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ed614aea8462735e7d70141374bd7650afd1c3f3cb0c2dbbcbe44e14331bf002"}, + {file = "contourpy-1.1.0-cp39-cp39-win32.whl", hash = "sha256:71551f9520f008b2950bef5f16b0e3587506ef4f23c734b71ffb7b89f8721999"}, {file = "contourpy-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:438ba416d02f82b692e371858143970ed2eb6337d9cdbbede0d8ad9f3d7dd17d"}, {file = "contourpy-1.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a698c6a7a432789e587168573a864a7ea374c6be8d4f31f9d87c001d5a843493"}, {file = "contourpy-1.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:397b0ac8a12880412da3551a8cb5a187d3298a72802b45a3bd1805e204ad8439"}, @@ -921,14 +948,17 @@ python-versions = ">=3.6" files = [ {file = "imgui-bundle-0.8.7.tar.gz", hash = "sha256:546cb787f1e5867abeb87a69870cdb591283a900e74b4c766a1e33b12ac36727"}, {file = "imgui_bundle-0.8.7-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:a47b127bffb4979413f9e6f7d24e9ce115d8440d453facfd4e85329a96bdbde4"}, + {file = "imgui_bundle-0.8.7-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:c7a5bafc81fbb9076edd4866a1079f0a8500aef1272be85430e69d536e40721d"}, {file = "imgui_bundle-0.8.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f277e192d0de34e54bff92194f6f1986a45b220f0f2d9ac8dd04f4ba0a642a"}, {file = "imgui_bundle-0.8.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d902bad4fc3e9f480eda9fb4a2afa505a31fef9104d02230fed0d38380fb3a32"}, {file = "imgui_bundle-0.8.7-cp310-cp310-win_amd64.whl", hash = "sha256:7ed4bf8aae943d6d84355155d7f256ca37793fe8fe5a96b73273805225023fc6"}, {file = "imgui_bundle-0.8.7-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:6a3f71cf33b3feb4351abab04e5b01e575ca69da429f9ec53c1f76b3d467b4ad"}, + {file = "imgui_bundle-0.8.7-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:ece852a729f01bccaaa0ab18d57c0a953d53acb5ad4c0bb18622deb4f97cfc09"}, {file = "imgui_bundle-0.8.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d0dfe7913a3a4ea7d7ff443216827be1550fe4a9b5d9310ca68126b5d80fffd"}, {file = "imgui_bundle-0.8.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c5f5e876f85f92ac3d8022a2da7b58604f968ec7fd4122d2f75a79f17818b19c"}, {file = "imgui_bundle-0.8.7-cp311-cp311-win_amd64.whl", hash = "sha256:22fdca289d25d7ddfd5f59ca3c50d612048bc050a305843f110065fe0ad220b7"}, {file = "imgui_bundle-0.8.7-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:a93a4b34d12776e7095294e7b0f9e3574174464980300e614e73ed608b6388e6"}, + {file = "imgui_bundle-0.8.7-cp39-cp39-macosx_13_0_arm64.whl", hash = "sha256:0c8c156675158e92fb11d5e5a655256add9b4c494599b414d23d812bc023c96f"}, {file = "imgui_bundle-0.8.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:628bf02535d417518988d845e81fdb6892257cd7977e19a7c0a1d92d94d1c41a"}, {file = "imgui_bundle-0.8.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7a213811188b2e4fd19e6f9d4e5dd5f5d709e6623b7e2c0a789cc7f300c39c9d"}, {file = "imgui_bundle-0.8.7-cp39-cp39-win_amd64.whl", hash = "sha256:a8612bb86e7993d57b43bfa970e84832eafb81dbeb89c218fe6cb3208ee25ba1"}, @@ -1191,6 +1221,7 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" files = [ {file = "jsonpointer-2.4-py2.py3-none-any.whl", hash = "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a"}, + {file = "jsonpointer-2.4.tar.gz", hash = "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88"}, ] [[package]] @@ -1666,6 +1697,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -3716,4 +3757,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.12" -content-hash = "f7baee857c94ab620b7ceb3208aab28a5279f36efef8e43a5f49bd35be8f11ec" +content-hash = "7271796b9197f6c2e768e0940faaaca33c62f3d1901a95dd0aae9a0412822bd8" diff --git a/py/pyproject.toml b/py/pyproject.toml index 18b83b5..79de81a 100644 --- a/py/pyproject.toml +++ b/py/pyproject.toml @@ -22,6 +22,8 @@ numba = "^0.57.0" jax = {extras = ["cpu"], version = "^0.4.12"} matplotlib = "^3.7.2" glom = "^23.3.0" +cattrs = "^23.2.3" +attrs = "^23.1.0" [tool.poetry.group.dev.dependencies] diff --git a/py/pytelem/skylab.py b/py/pytelem/skylab.py index 63812ff..4190fd2 100644 --- a/py/pytelem/skylab.py +++ b/py/pytelem/skylab.py @@ -3,13 +3,31 @@ from abc import ABC, abstractmethod import re from pathlib import Path -from typing import Callable, Iterable, NewType, TypedDict, List, Protocol, Union, Set +from typing import Annotated, Callable, Iterable, Literal, NewType, TypedDict, List, Protocol, Union, Set, Optional from pydantic import field_validator, BaseModel, validator, model_validator +from pydantic.functional_validators import AfterValidator from enum import Enum import yaml import jinja2 +def name_valid(s: str) -> str: + if len(s) == 0: + raise ValueError("name cannot be empty string") + if not re.match(r"^[A-Za-z_][A-Za-z0-9_]?$", s): + raise ValueError(f"invalid name: {s}") + return s + +# ObjectName is a string that is a valid name, it can only be alphanumeric and underscore. +# it must start with +ObjectName = Annotated[str, AfterValidator(name_valid)] + +def is_valid_can_id(i: int) -> int: + if i < 0: + raise ValueError("CAN ID cannot be negative") + return i + +CanID = Annotated[int, AfterValidator(is_valid_can_id)] # This part of the file is dedicated to parsing the skylab yaml files. We define # classes that represent objects in the yaml files, and perform basic validation on @@ -31,7 +49,6 @@ class FieldType(str, Enum): I64 = "int64_t" F32 = "float" - Bitfield = "bitfield" def size(self) -> int: """Returns the size, in bytes, of the type.""" @@ -54,8 +71,6 @@ class FieldType(str, Enum): return 8 case FieldType.F32: return 4 - case FieldType.Bitfield: - return 1 return -1 @@ -69,68 +84,43 @@ class _Bits(TypedDict): name: str +class BitField(BaseModel): + name: ObjectName + type: Literal["bitfield"] + bits: List[_Bits] -class SkylabField(BaseModel): +class EnumField(BaseModel): + name: ObjectName + type: Literal["enum"] + enum_reference: str + "The name of the custom enum to use" + + +class BasicField(BaseModel): """Represents a field (data element) inside a Skylab Packet.""" - name: str - "the name of the field. must be alphanumeric and underscores" + name: ObjectName type: FieldType "the type of the field" - units: str | None = None + units: Optional[str] "optional descriptor of the unit representation" - conversion: float | None = None + conversion: Optional[float] "optional conversion factor to be applied when parsing" - bits: List[_Bits] | None = None - "if the type if a bitfield, " - - @model_validator(mode='after') - def bits_must_exist_if_bitfield(self) -> 'SkylabField': - if self.bits is None and self.type == FieldType.Bitfield: - raise ValueError("bits are not present on bitfield type") - if self.bits is not None and self.type != FieldType.Bitfield: - raise ValueError("bits are present on non-bitfield type") - return self - - @field_validator("name") - @classmethod - def name_valid_string(cls, v: str) -> str: - if not re.match(r"^[A-Za-z0-9_]+$", v): - raise ValueError("invalid name") - return v - - @field_validator("name") - @classmethod - def name_nonzero_length(cls, v: str) -> str: - if len(v) == 0: - raise ValueError("name cannot be empty string") - return v -class Endian(str, Enum): - """Symbol representing the endianness of the packet""" - - Big = "big" - Little = "little" - +SkylabField = Union[BasicField, EnumField, BitField] class SkylabPacket(BaseModel): """Represents a CAN packet. Contains SkylabFields with information on the structure of the data.""" - name: str + name: ObjectName description: str | None = None - id: int - endian: Endian + id: CanID + endian: Literal["big", "little"] repeat: int | None = None offset: int | None = None data: List[SkylabField] - # @validator("data") - # def packet_size_limit(cls, v: List[SkylabField]): - # tot = sum([f.type.size() for f in v]) - # if tot > 8: - # return ValueError("Total packet size cannot exceed 8 bytes") - # return v @field_validator("id") @classmethod @@ -139,34 +129,16 @@ class SkylabPacket(BaseModel): raise ValueError("id must be above zero") return v - @field_validator("name") - @classmethod - def name_valid_string(cls, v: str) -> str: - if not re.match(r"^[A-Za-z0-9_]+$", v): - raise ValueError("invalid name", v) - return v - @field_validator("name") - @classmethod - def name_nonzero_length(cls, v: str) -> str: - if len(v) == 0: - raise ValueError("name cannot be empty string") - return v +class RepeatedPacket(BaseModel): + name: ObjectName + description: str | None = None + id: CanID + endian: Literal["big", "little"] + repeat: int + offset: int + data: List[SkylabField] - # TODO[pydantic]: We couldn't refactor the `validator`, please replace it by `field_validator` manually. - # Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-validators for more information. - @model_validator(mode='after') - def offset_must_have_repeat(self) -> "SkylabPacket": - if self.offset is not None and self.repeat is not None: - raise ValueError("field with offset must have repeat defined") - return v - - @field_validator("repeat") - @classmethod - def repeat_gt_one(cls, v: int | None): - if v is not None and v <= 1: - raise ValueError("repeat must be strictly greater than one") - return v class SkylabBoard(BaseModel): @@ -177,43 +149,23 @@ class SkylabBoard(BaseModel): - every name in the transmit/receive list must have a corresponding packet. """ - name: str + name: ObjectName "The name of the board" transmit: List[str] "The packets sent by this board" receive: List[str] "The packets received by this board." - @field_validator("name") - @classmethod - def name_valid_string(cls, v: str): - if not re.match(r"^[A-Za-z0-9_]+$", v): - return ValueError("invalid name", v) - return v - - @field_validator("name") - @classmethod - def name_nonzero_length(cls, v: str): - if len(v) == 0: - return ValueError("name cannot be empty string") - return v class SkylabBus(BaseModel): - name: str + name: ObjectName "The name of the bus" baud_rate: int "Baud rate setting for the bus" extended_id: bool "If the bus uses extended ids" - @field_validator("name") - @classmethod - def name_valid_string(cls, v: str): - if not re.match(r"^[A-Za-z0-9_]+$", v): - return ValueError("invalid name", v) - return v - @field_validator("baud_rate") @classmethod def baud_rate_supported(cls, v: int): diff --git a/py/pytelem/skylab_attr.py b/py/pytelem/skylab_attr.py new file mode 100644 index 0000000..4b17230 --- /dev/null +++ b/py/pytelem/skylab_attr.py @@ -0,0 +1,112 @@ + +from typing import Dict, Optional, List, Union +from enum import Enum +from attrs import define, field, validators + + +@define +class Bus(): + name: str + baud_rate: str + extended_id: bool = False + + + + +class FieldType(str, Enum): + """FieldType indicates the type of the field - the enum represents the C type, + but you can use a map to convert the type to another language.""" + + # used to ensure types are valid, and act as representations for other languages/mappings. + U8 = "uint8_t" + U16 = "uint16_t" + U32 = "uint32_t" + U64 = "uint64_t" + I8 = "int8_t" + I16 = "int16_t" + I32 = "int32_t" + I64 = "int64_t" + F32 = "float" + + Bitfield = "bitfield" + + def size(self) -> int: + """Returns the size, in bytes, of the type.""" + match self: + case FieldType.U8: + return 1 + case FieldType.U16: + return 2 + case FieldType.U32: + return 4 + case FieldType.U64: + return 8 + case FieldType.I8: + return 1 + case FieldType.I16: + return 2 + case FieldType.I32: + return 4 + case FieldType.I64: + return 8 + case FieldType.F32: + return 4 + case FieldType.Bitfield: + return 1 + return -1 + + +@define +class CustomTypeDef(): + name: str + base_type: FieldType # should be a strict size + values: Union[List[str], Dict[str, int]] + + +@define +class BitfieldBit(): + "micro class to represent one bit in bitfields" + name: str + +@define +class Field(): + name: str = field(validator=[validators.matches_re(r"^[A-Za-z0-9_]+$")]) + type: FieldType + + #metadata + units: Optional[str] + conversion: Optional[float] + + +@define +class BitField(): + name: str = field(validator=[validators.matches_re(r"^[A-Za-z0-9_]+$")]) + type: str = field(default="bitfield", init=False) # it's a constant value + bits: List[BitfieldBit] + + +class Endian(str, Enum): + BIG = "big" + LITTLE = "little" + +@define +class Packet(): + name: str + description: str + id: int + endian: Endian + frequency: Optional[int] + data: List[Field] + +@define +class RepeatedPacket(): + name: str + description: str + id: int + endian: Endian + frequency: Optional[int] + data: List[Field] + repeat: int + offset: int + +