Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update/new data models #67

Merged
merged 15 commits into from
May 13, 2022
Merged
4 changes: 4 additions & 0 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ autoapi
autocoded
autocode
Autocoders
autocoders
autocoding
autodoc
autoescape
Expand Down Expand Up @@ -118,6 +119,7 @@ isdir
isfile
isinstance
isnumeric
issubclass
iterdir
itertools
itle
Expand All @@ -136,6 +138,7 @@ lestarch
LGTM
lgtm
Linux
lld
locs
lstrip
lxml
Expand Down Expand Up @@ -200,6 +203,7 @@ SCLK
scm
sdd
Serializables
serializables
setuptools
shutil
someotherpath
Expand Down
105 changes: 42 additions & 63 deletions src/fprime/common/models/serialize/array_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,46 @@
Created on May 29, 2020
@author: jishii
"""
import copy

from .type_base import ValueType
from .type_base import DictionaryType
from .type_exceptions import (
ArrayLengthException,
NotInitializedException,
TypeMismatchException,
)

from . import serializable_type
from fprime.util.string_util import format_string_template


class ArrayType(ValueType):
class ArrayType(DictionaryType):
"""Generic fixed-size array type representation.

Represents a custom named type of a fixed number of like members, each of which are other types in the system.
"""

def __init__(self, typename, config_info, val=None):
"""Constructs a new array type.
@classmethod
def construct_type(cls, name, member_type, length, format):
"""Constructs a sub-array type

Constructs a new sub-type of array to represent an array of the given name, member type, length, and format
string.

Args:
typename: name of this array type
config_info: (type, size, format string) information for array
val: (optional) list of values to assign to array
name: name of the array subtype
member_type: type of the members of the array subtype
length: length of the array subtype
format: format string for members of the array subtype
"""
super().__init__()
if not isinstance(typename, str):
raise TypeMismatchException(str, type(typename))
self.__val = None
self.__typename = typename
self.__arr_type, self.__arr_size, self.__arr_format = config_info
# Set value only if it is a valid, non-empty list
if not val:
return
self.val = val

def validate(self, val):
return DictionaryType.construct_type(
cls, name, MEMBER_TYPE=member_type, LENGTH=length, FORMAT=format
)

@classmethod
def validate(cls, val):
"""Validates the values of the array"""
size = self.__arr_size
if len(val) != size:
raise ArrayLengthException(self.__arr_type, size, len(val))
for i in range(self.__arr_size):
if not isinstance(val[i], type(self.__arr_type)):
raise TypeMismatchException(type(self.__arr_type), type(val[i]))
if len(val) != cls.LENGTH:
LeStarch marked this conversation as resolved.
Show resolved Hide resolved
raise ArrayLengthException(cls.MEMBER_TYPE, cls.LENGTH, len(val))
for i in range(cls.LENGTH):
cls.MEMBER_TYPE.validate(val[i])

@property
def val(self) -> list:
Expand All @@ -58,7 +52,9 @@ def val(self) -> list:

:return dictionary of member names to python values of member keys
"""
return [item.val for item in self.__val]
if self._val is None:
return None
return [item.val for item in self._val]

@property
def formatted_val(self) -> list:
Expand All @@ -69,11 +65,11 @@ def formatted_val(self) -> list:
:return a formatted array
"""
result = []
for item in self.__val:
for item in self._val:
if isinstance(item, (serializable_type.SerializableType, ArrayType)):
result.append(item.formatted_val)
else:
result.append(format_string_template(self.__arr_format, item.val))
result.append(format_string_template(self.FORMAT, item.val))
return result

@val.setter
Expand All @@ -85,58 +81,41 @@ def val(self, val: list):

:param val: dictionary containing python types to key names. This
"""
items = []
for item in val:
cloned = copy.deepcopy(self.arr_type)
cloned.val = item
items.append(cloned)
self.__val = items
self.validate(val)
items = [self.MEMBER_TYPE(item) for item in val]
self._val = items

def to_jsonable(self):
LeStarch marked this conversation as resolved.
Show resolved Hide resolved
"""
JSONable type
"""
members = {
"name": self.__typename,
"type": self.__typename,
"size": self.__arr_size,
"format": self.__arr_format,
"name": self.__class__.__name__,
"type": self.__class__.__name__,
"size": self.LENGTH,
"format": self.FORMAT,
"values": None
if self.__val is None
else [member.to_jsonable() for member in self.__val],
if self._val is None
else [member.to_jsonable() for member in self._val],
}
return members

def serialize(self):
"""Serialize the array by serializing the elements one by one"""
if self.val is None:
raise NotInitializedException(type(self))
return b"".join([item.serialize() for item in self.val])
return b"".join([item.serialize() for item in self._val])

def deserialize(self, data, offset):
"""Deserialize the members of the array"""
values = []
for i in range(self.__arr_size):
item = copy.deepcopy(self.arr_type)
item.deserialize(data, offset + i * item.getSize())
for i in range(self.LENGTH):
item = self.MEMBER_TYPE()
item.deserialize(data, offset)
offset += item.getSize()
values.append(item.val)
self.val = values

@property
def arr_type(self):
"""Property representing the size of the array"""
return self.__arr_type

@property
def arr_size(self):
"""Property representing the number of elements of the array"""
return self.__arr_size

@property
def arr_format(self):
"""Property representing the format string of an item in the array"""
return self.__arr_format

def getSize(self):
"""Return the size of the array"""
return self.arr_type.getSize() * self.arr_size
return sum([item.getSize() for item in self._val])
9 changes: 5 additions & 4 deletions src/fprime/common/models/serialize/bool_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,25 @@ class BoolType(ValueType):
TRUE = 0xFF
FALSE = 0x00

def validate(self, val):
@classmethod
def validate(cls, val):
"""Validate the given class"""
if not isinstance(val, bool):
raise TypeMismatchException(bool, type(val))

def serialize(self):
"""Serialize a boolean value"""
if self.val is None:
if self._val is None:
LeStarch marked this conversation as resolved.
Show resolved Hide resolved
raise NotInitializedException(type(self))
return struct.pack("B", 0xFF if self.val else 0x00)
return struct.pack("B", self.TRUE if self._val else self.FALSE)

def deserialize(self, data, offset):
"""Deserialize boolean value"""
try:
int_val = struct.unpack_from("B", data, offset)[0]
if int_val not in [self.TRUE, self.FALSE]:
raise TypeRangeException(int_val)
self.val = int_val == self.TRUE
self._val = int_val == self.TRUE
except struct.error:
raise DeserializeException("Not enough bytes to deserialize bool.")

Expand Down
79 changes: 38 additions & 41 deletions src/fprime/common/models/serialize/enum_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"""
import struct

from .type_base import ValueType
from .type_base import DictionaryType
from .type_exceptions import (
DeserializeException,
EnumMismatchException,
Expand All @@ -14,7 +14,7 @@
)


class EnumType(ValueType):
class EnumType(DictionaryType):
"""
Representation of the ENUM type.

Expand All @@ -23,61 +23,58 @@ class EnumType(ValueType):
containing code based on C enum rules
"""

def __init__(self, typename="", enum_dict=None, val=None):
"""
Constructor
def __init__(self, val="UNDEFINED"):
"""Construct the enumeration value, called through sub-type constructor

:param typename: name of the enumeration type
:param enum_dict: dictionary of value to integer representation
:param val: value of the enumeration
Args:
val: (optional) value this instance of enumeration is set to. Default: "UNDEFINED"
"""
super().__init__()
if not isinstance(typename, str):
raise TypeMismatchException(str, type(val))
self.__typename = typename
# Setup the enum dictionary
if enum_dict is None:
enum_dict = {"UNDEFINED": 0}
# Check if the enum dict is an instance of dictionary
self.__enum_dict = enum_dict
# Set val to undefined if not set
if val is None:
val = "UNDEFINED"
self.val = val
super().__init__(val)

def validate(self, val):
"""Validate the value passed into the enumeration"""
if not isinstance(self.enum_dict(), dict):
raise TypeMismatchException(dict, type(self.enum_dict()))
for member in self.keys():
@classmethod
def construct_type(cls, name, enum_dict=None):
"""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
"""
enum_dict = enum_dict if enum_dict is not None else {"UNDEFINED": 0}
if not isinstance(enum_dict, dict):
raise TypeMismatchException(dict, type(enum_dict))
for member in enum_dict.keys():
if not isinstance(member, str):
raise TypeMismatchException(str, type(member))
elif not isinstance(self.enum_dict()[member], int):
raise TypeMismatchException(int, self.enum_dict()[member])
if val != "UNDEFINED" and val not in self.keys():
raise EnumMismatchException(self.__typename, val)
elif not isinstance(enum_dict[member], int):
raise TypeMismatchException(int, enum_dict[member])
return DictionaryType.construct_type(cls, name, ENUM_DICT=enum_dict)

def keys(self):
@classmethod
def validate(cls, val):
"""Validate the value passed into the enumeration"""
if val != "UNDEFINED" and val not in cls.keys():
raise EnumMismatchException(cls.__class__.__name__, val)

@classmethod
def keys(cls):
"""
Return all the enum key values.
"""
return list(self.enum_dict().keys())

def typename(self):
return self.__typename

def enum_dict(self):
return self.__enum_dict
return list(cls.ENUM_DICT.keys())

def serialize(self):
"""
Serialize the enumeration type using an int type
"""
# for enums, take the string value and convert it to
# the numeric equivalent
if self.val is None:
if self._val is None or (
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(">i", self.ENUM_DICT[self.val])
LeStarch marked this conversation as resolved.
Show resolved Hide resolved

def deserialize(self, data, offset):
"""
Expand All @@ -89,7 +86,7 @@ def deserialize(self, data, offset):
raise DeserializeException(
f"Could not deserialize enum value. Needed: {self.getSize()} bytes Found: {len(data[offset:])}"
)
for key, val in self.enum_dict().items():
for key, val in self.ENUM_DICT.items():
if int_val == val:
self.val = key
break
Expand Down
Loading