Skip to content

Commit

Permalink
Fix/enum representation types (#184)
Browse files Browse the repository at this point in the history
* JackNWhite: Added representation types to enumerations, with checks, and (de)serialisation + associated exceptions

* JackNWhite: Fixed blatent typos in previous commit

* Jack White: Moved F' basic types enum to a new script

* Jack White: Added variations on Endian to check-spelling allowed dictionary

* Jack White: Ran black on prior changes

* Jack White: Ran black on other prior changes

* Jack White: Added default representation type to EnumType.construct_type()

* Jack White: Updated EnumType.getMaxSize() to return size of the representation type

* Replacing static metadata with class defined properties

* Formatting

---------

Co-authored-by: Jack White <jack.white@redwirespaceeurope.com>
Co-authored-by: M Starch <LeStarch@googlemail.com>
  • Loading branch information
3 people committed Feb 16, 2024
1 parent b3b98b7 commit 3efb736
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 8 deletions.
4 changes: 4 additions & 0 deletions .github/actions/spelling/allow.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ calcsize
changelog
chdir
classmethod
Endian
endian
Endianness
endianness
lestarch
LGTM
lgtm
Expand Down
50 changes: 42 additions & 8 deletions src/fprime/common/models/serialize/enum_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@
NotInitializedException,
TypeMismatchException,
TypeRangeException,
InvalidRepresentationTypeException,
RepresentationTypeRangeException,
)
from .numerical_types import IntegerType

REPRESENTATION_TYPE_MAP = {
cls.get_canonical_name(): cls for cls in IntegerType.__subclasses__()
}


class EnumType(DictionaryType):
Expand All @@ -24,14 +31,15 @@ class EnumType(DictionaryType):
"""

@classmethod
def construct_type(cls, name, enum_dict):
def construct_type(cls, name, enum_dict, rep_type="I32"):
"""Construct the custom enum type
Constructs the custom enumeration type, with the supplied enumeration dictionary.
Args:
name: name of the enumeration type
enum_dict: enumeration: value dictionary defining the enumeration
rep_type: representation type (standard Fprime integer types)
"""
if not isinstance(enum_dict, dict):
raise TypeMismatchException(dict, type(enum_dict))
Expand All @@ -40,7 +48,22 @@ def construct_type(cls, name, enum_dict):
raise TypeMismatchException(str, type(member))
if not isinstance(enum_dict[member], int):
raise TypeMismatchException(int, enum_dict[member])
return DictionaryType.construct_type(cls, name, ENUM_DICT=enum_dict)

if rep_type not in REPRESENTATION_TYPE_MAP.keys():
raise InvalidRepresentationTypeException(
rep_type, REPRESENTATION_TYPE_MAP.keys()
)

for member in enum_dict.keys():
type_range = REPRESENTATION_TYPE_MAP[rep_type].range()
if enum_dict[member] < type_range[0] or enum_dict[member] > type_range[1]:
raise RepresentationTypeRangeException(
member, enum_dict[member], rep_type, type_range
)

return DictionaryType.construct_type(
cls, name, ENUM_DICT=enum_dict, REP_TYPE=rep_type
)

@classmethod
def validate(cls, val):
Expand All @@ -67,14 +90,22 @@ def serialize(self):
self._val == "UNDEFINED" and "UNDEFINED" not in self.ENUM_DICT
):
raise NotInitializedException(type(self))
return struct.pack(">i", self.ENUM_DICT[self._val])
return struct.pack(
REPRESENTATION_TYPE_MAP[self.REP_TYPE].get_serialize_format(),
self.ENUM_DICT[self._val],
)

def deserialize(self, data, offset):
"""
Deserialize the enumeration using an int type
"""
try:
int_val = struct.unpack_from(">i", data, offset)[0]
int_val = struct.unpack_from(
REPRESENTATION_TYPE_MAP[self.REP_TYPE].get_serialize_format(),
data,
offset,
)[0]

except struct.error:
msg = f"Could not deserialize enum value. Needed: {self.getSize()} bytes Found: {len(data[offset:])}"
raise DeserializeException(msg)
Expand All @@ -86,12 +117,15 @@ def deserialize(self, data, offset):
else:
raise TypeRangeException(int_val)

@classmethod
def getSize(cls):
def getSize(self):
"""Calculates the size based on the size of an integer used to store it"""
return struct.calcsize(">i")
return struct.calcsize(
REPRESENTATION_TYPE_MAP[self.REP_TYPE].get_serialize_format()
)

@classmethod
def getMaxSize(cls):
"""Maximum size of type"""
return cls.getSize() # Always the same as getSize
return struct.calcsize(
REPRESENTATION_TYPE_MAP[cls.REP_TYPE].get_serialize_format()
)
5 changes: 5 additions & 0 deletions src/fprime/common/models/serialize/numerical_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
class NumericalType(ValueType, abc.ABC):
"""Numerical types that can be serialized using struct and are of some power of 2 byte width"""

@classmethod
def get_canonical_name(cls):
"""Returns the fprime C++ name for the type"""
return cls.__name__.replace("Type", "")

@classmethod
@abc.abstractmethod
def get_bits(cls):
Expand Down
20 changes: 20 additions & 0 deletions src/fprime/common/models/serialize/type_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,23 @@ def __init__(self, field_length_actual, field_length_given):
"%d fields provided, but compound type expects %d fields!"
% (field_length_given, field_length_actual)
)


class InvalidRepresentationTypeException(TypeException):
"""Representation type of the given enumeration is not an F prime integer type"""

def __init__(self, given_rep_type, types):
super().__init__(
"Representation type {} not found in F prime types ({})"
).format(given_rep_type, str(types))


class RepresentationTypeRangeException(TypeException):
"""Enumeration member is out of range of the representation type"""

def __init__(self, key, value, given_rep_type, range):
super().__init__(
"Enumeration member {} with value {} is out of range of representation type {} ({}-{})".format(
key, value, given_rep_type, *range
)
)

0 comments on commit 3efb736

Please sign in to comment.