mirror of
https://github.com/AdaCore/OpenUxAS.git
synced 2026-02-12 13:07:16 -08:00
This commit makes significant effort towards refactoring the repository so that it is in a cleaner and more consistent state going forward. Since we are now explicitly expecting multiple languages to be used for OpenUxAS, we have reorganized `src` accordingly. Likewise, we have reorganized `tests`. This is a candidate for the rebaseline of afrl-rq/OpenUxAS.
153 lines
4.8 KiB
Python
153 lines
4.8 KiB
Python
import struct
|
|
import typing
|
|
from typing import Tuple, List, Optional
|
|
from pylmcp.model.object_attr import ObjectAttr
|
|
|
|
if typing.TYPE_CHECKING:
|
|
import pylmcp.model
|
|
import pylmcp.model.series
|
|
import pylmcp.util
|
|
|
|
|
|
class ObjectClass(object):
|
|
"""Message class (i.e: Struct)."""
|
|
|
|
def __init__(self,
|
|
name: str,
|
|
id: int,
|
|
extends: typing.Optional[str],
|
|
attrs: typing.List[ObjectAttr],
|
|
series: "pylmcp.model.series.Series",
|
|
model_db: "pylmcp.model.ModelDatabase") -> None:
|
|
"""Initialize an LMCP class.
|
|
|
|
:param name: class name
|
|
:param id: an integer id for the class
|
|
:param extends: a class name from which this class is derived.
|
|
The class name is in the format SERIES_NAME/CLASS_NAME
|
|
:param attrs: a list of ObjAttr instance defining the attributes
|
|
for the this class
|
|
:param series: the series in which this class is defined
|
|
:param model_db: the model database
|
|
"""
|
|
self.name = name
|
|
self.__attrs = attrs
|
|
self.extends = extends
|
|
self.series = series
|
|
self.model_db = model_db
|
|
self.id = id
|
|
|
|
# Full type name in dot notation
|
|
self.full_name = "%s.%s" % (self.namespace.replace('/', '.'), name)
|
|
|
|
@property
|
|
def attrs(self) -> typing.List[ObjectAttr]:
|
|
"""Return the full list of attributes.
|
|
|
|
:return: the complete list of attributes (including the attributes
|
|
declared in the parent classes)
|
|
"""
|
|
result: typing.List[ObjectAttr] = []
|
|
if self.extends:
|
|
result += self.model_db.types[self.extends].attrs
|
|
result += self.__attrs
|
|
return result
|
|
|
|
@property
|
|
def full_id(self) -> Tuple[int, int, int]:
|
|
"""Return a tuple that represents the full class id."""
|
|
return (self.series_id, self.id, self.version)
|
|
|
|
@property
|
|
def series_id(self) -> int:
|
|
"""Return the series id."""
|
|
return self.series.id
|
|
|
|
@property
|
|
def series_name(self) -> str:
|
|
"""Return the series name."""
|
|
return self.series.name
|
|
|
|
@property
|
|
def namespace(self) -> str:
|
|
"""Return the namespace in which the class is declared."""
|
|
return self.series.namespace
|
|
|
|
@property
|
|
def version(self) -> int:
|
|
"""The class version (i.e same as series version)."""
|
|
return self.series.version
|
|
|
|
def pack(self,
|
|
value: dict,
|
|
include_headers: bool = True) -> bytes:
|
|
"""Pack the message into its binary format.
|
|
|
|
:param value: attribute's values
|
|
:param include_headers: add LMCP header
|
|
:return: the binary representation
|
|
"""
|
|
|
|
# Starts with the object type
|
|
result = struct.pack(">qIH", *self.full_id)
|
|
|
|
# Followed by the attributes
|
|
for attr in self.attrs:
|
|
result += attr.pack(value=value[attr.name])
|
|
|
|
if include_headers:
|
|
# ??? not complete ???. The constant there should be computed
|
|
header = struct.pack(">iIB", 1280131920, len(result), 1)
|
|
result = header + result
|
|
# Compute the final checksum and append it
|
|
sum = 0
|
|
for x in range(len(result)):
|
|
sum += result[x] & 0xFF
|
|
result += struct.pack(">I", sum)
|
|
return result
|
|
else:
|
|
return result
|
|
|
|
@classmethod
|
|
def unpack(self,
|
|
payload: "pylmcp.util.Buffer") -> Optional[Tuple[int, int, int]]:
|
|
exists = payload.unpack("bool")
|
|
if not exists:
|
|
return None
|
|
else:
|
|
full_id = typing.cast(
|
|
Tuple[int, int, int],
|
|
payload.unpack_struct(">qIH"))
|
|
return full_id
|
|
|
|
@classmethod
|
|
def from_xml(cls, node, id, series, model_db):
|
|
# Parse the object attributes
|
|
attrs = []
|
|
for attr in node:
|
|
attrs.append(ObjectAttr.from_xml(attr, series, model_db))
|
|
|
|
# Get the the parent class
|
|
extends = node.attrib.get('Extends', None)
|
|
series_name = node.attrib.get('Series', None)
|
|
if extends is not None:
|
|
if "/" not in extends:
|
|
if series_name is not None:
|
|
extends = "%s/%s" % (series_name, extends)
|
|
else:
|
|
extends = "%s/%s" % (series.name, extends)
|
|
|
|
# Return the new class
|
|
return cls(
|
|
name=node.attrib['Name'],
|
|
id=id,
|
|
extends=extends,
|
|
attrs=attrs,
|
|
series=series,
|
|
model_db=model_db)
|
|
|
|
def __str__(self) -> str:
|
|
return "\n".join(["lmcp_type: %s" % self.id,
|
|
"series_name: %s" % self.series_name,
|
|
"full_lmcp_type_name: %s" % self.full_name])
|