Init Commit: Moved bproto to seperate repo
This commit is contained in:
244
src/backend/rendering/pythonBackend.py
Normal file
244
src/backend/rendering/pythonBackend.py
Normal file
@@ -0,0 +1,244 @@
|
||||
from backend import BackendRenderer
|
||||
from protocol_components.dtypes import BprotoFieldBaseType
|
||||
from protocol_components.protocol import ProtocolDefinitions
|
||||
from protocol_components.field import Field, FieldBitfield, FieldEnum
|
||||
from protocol_components.crc import CRC_SIZE
|
||||
|
||||
from backend.fsOutput import BackendFileSystemOutput, BackendFSOutputJinjaFile, BackendFSOutFolder, BackendFSOutStaticConent
|
||||
from nameHandling.style.pythonNameStyle import NameStylePython
|
||||
from nameHandling.base import ComponentName
|
||||
|
||||
from jinja2 import Environment
|
||||
from copy import deepcopy
|
||||
|
||||
ENDIANDNESS = "<"
|
||||
|
||||
PYTHON_DATA_TYPE_MAP = {
|
||||
BprotoFieldBaseType.BOOL: ("bool", False),
|
||||
|
||||
BprotoFieldBaseType.UINT8: ("int", 0),
|
||||
BprotoFieldBaseType.UINT16: ("int", 0),
|
||||
BprotoFieldBaseType.UINT32: ("int", 0),
|
||||
BprotoFieldBaseType.UINT64: ("int", 0),
|
||||
|
||||
BprotoFieldBaseType.INT8: ("int", 0),
|
||||
BprotoFieldBaseType.INT16: ("int", 0),
|
||||
BprotoFieldBaseType.INT32: ("int", 0),
|
||||
BprotoFieldBaseType.INT64: ("int", 0),
|
||||
|
||||
BprotoFieldBaseType.FLOAT32: ("float", 0.0),
|
||||
BprotoFieldBaseType.FLOAT64: ("float", 0.0),
|
||||
|
||||
BprotoFieldBaseType.CHAR: ("str", "''"),
|
||||
BprotoFieldBaseType.STRING: ("str", "''"),
|
||||
|
||||
BprotoFieldBaseType.BITFIELD: ("", ""),
|
||||
BprotoFieldBaseType.ENUM: ("", ""),
|
||||
}
|
||||
|
||||
PYTHON_DATA_TYPE_STRUCT_FORMAT = {
|
||||
BprotoFieldBaseType.BOOL: "B",
|
||||
|
||||
BprotoFieldBaseType.UINT8: "B",
|
||||
BprotoFieldBaseType.UINT16: "H",
|
||||
BprotoFieldBaseType.UINT32: "I",
|
||||
BprotoFieldBaseType.UINT64: "Q",
|
||||
|
||||
BprotoFieldBaseType.INT8: "b",
|
||||
BprotoFieldBaseType.INT16: "h",
|
||||
BprotoFieldBaseType.INT32: "i",
|
||||
BprotoFieldBaseType.INT64: "q",
|
||||
|
||||
BprotoFieldBaseType.FLOAT32: "f",
|
||||
BprotoFieldBaseType.FLOAT64: "d",
|
||||
}
|
||||
|
||||
|
||||
def map_to_bytes_conversion(field: Field, endiend: str) -> str:
|
||||
name = NameStylePython.toStr(field.name, "class_member")
|
||||
|
||||
if isinstance(field, FieldEnum):
|
||||
return f"struct.pack('{endiend}B', self.{name}.value)"
|
||||
|
||||
elif isinstance(field, FieldBitfield):
|
||||
return f"self.{name}.to_bytes()"
|
||||
|
||||
elif field.type in PYTHON_DATA_TYPE_STRUCT_FORMAT:
|
||||
if field.array_size > 1:
|
||||
argument = ", ".join([
|
||||
f"self.{name}[{i}]"
|
||||
for i in range(field.array_size)
|
||||
])
|
||||
return f"struct.pack('{endiend}{PYTHON_DATA_TYPE_STRUCT_FORMAT[field.type] * field.array_size}', {argument})"
|
||||
else:
|
||||
return f"struct.pack('{endiend}{PYTHON_DATA_TYPE_STRUCT_FORMAT[field.type]}', self.{name})"
|
||||
|
||||
elif field.type == BprotoFieldBaseType.STRING or field.type == BprotoFieldBaseType.CHAR:
|
||||
return f"self.{name}.encode('ascii')[:{field.array_size}].ljust({field.array_size}, b'\\x00')"
|
||||
|
||||
else:
|
||||
raise TypeError(f"Cannot convert field {field} to python to bytes conversion string")
|
||||
|
||||
|
||||
def map_from_bytes_conversion(field: Field, endiend: str) -> str:
|
||||
if isinstance(field, FieldEnum):
|
||||
return f"{NameStylePython.toStr(field.enum.name, "enum_name")}(struct.unpack('{endiend}B', data[:{field.get_size_bytes()}])[0])"
|
||||
|
||||
elif isinstance(field, FieldBitfield):
|
||||
return f"{NameStylePython.toStr(field.bitfield.name, "class_name")}().from_bytes(data[:{field.get_size_bytes()}])"
|
||||
|
||||
elif field.type == BprotoFieldBaseType.STRING or field.type == BprotoFieldBaseType.CHAR:
|
||||
return f"data[:{field.get_size_bytes()}].decode('ascii').strip('\\x00')"
|
||||
|
||||
elif field.type == BprotoFieldBaseType.BOOL:
|
||||
if field.array_size > 1:
|
||||
return f"[bool(i) for i in struct.unpack('{endiend}{PYTHON_DATA_TYPE_STRUCT_FORMAT[field.type] * field.array_size}', data[:{field.get_size_bytes()}])]"
|
||||
else:
|
||||
return f"bool(struct.unpack('>{PYTHON_DATA_TYPE_STRUCT_FORMAT[field.type]}', data[:{field.get_size_bytes()}])[0])"
|
||||
|
||||
elif field.type in PYTHON_DATA_TYPE_STRUCT_FORMAT:
|
||||
if field.array_size > 1:
|
||||
return f"list(struct.unpack('{endiend}{PYTHON_DATA_TYPE_STRUCT_FORMAT[field.type] * field.array_size}', data[:{field.get_size_bytes()}]))"
|
||||
else:
|
||||
return f"struct.unpack('{endiend}{PYTHON_DATA_TYPE_STRUCT_FORMAT[field.type]}', data[:{field.get_size_bytes()}])[0]"
|
||||
|
||||
else:
|
||||
raise TypeError(f"Cannot convert field {field} to python to bytes conversion string")
|
||||
|
||||
|
||||
def map_data_type_anotation(field: Field) -> str:
|
||||
if isinstance(field, FieldEnum):
|
||||
return f"{NameStylePython.toStr(field.enum.name, "enum_name")}"
|
||||
|
||||
elif isinstance(field, FieldBitfield):
|
||||
return f"{NameStylePython.toStr(field.bitfield.name, "class_name")}"
|
||||
|
||||
elif field.type in PYTHON_DATA_TYPE_STRUCT_FORMAT:
|
||||
if field.array_size > 1:
|
||||
return f"Annotated[List[{PYTHON_DATA_TYPE_MAP[field.type][0]}], {field.array_size}]"
|
||||
else:
|
||||
return f"{PYTHON_DATA_TYPE_MAP[field.type][0]}"
|
||||
|
||||
elif field.type == BprotoFieldBaseType.STRING or field.type == BprotoFieldBaseType.CHAR:
|
||||
return "str"
|
||||
|
||||
else:
|
||||
raise TypeError(f"Cannot convert field {field} to python to bytes conversion string")
|
||||
|
||||
|
||||
class PythonBackendRenderer(BackendRenderer):
|
||||
|
||||
def __init__(self, output_folder):
|
||||
self.output_folder = output_folder
|
||||
|
||||
self.protocol: ProtocolDefinitions = None
|
||||
self.jinja_env: Environment = None
|
||||
self.jinja_context: dict = None
|
||||
|
||||
def render(self,
|
||||
protocol_definition: ProtocolDefinitions,
|
||||
jinja_env: Environment) -> BackendFileSystemOutput:
|
||||
|
||||
if protocol_definition.protocol_hash_inital is None:
|
||||
raise ValueError("Protocol hash inital for crc16 should no be None")
|
||||
|
||||
self.protocol = NameStylePython.preprocess(deepcopy(protocol_definition))
|
||||
|
||||
self.jinja_env = jinja_env
|
||||
self.jinja_context = {
|
||||
"protocol": {
|
||||
"name": NameStylePython.toStr(self.protocol.name, "enum_item"),
|
||||
"version": self.protocol.version,
|
||||
"crc_size": CRC_SIZE,
|
||||
"crc_initial": hex(self.protocol.protocol_hash_inital),
|
||||
"endian_format": ENDIANDNESS,
|
||||
"endian_str": "little" if ENDIANDNESS == "<" else "big"
|
||||
},
|
||||
"import_name": NameStylePython.toStr(self.protocol.name, 'enum_item'),
|
||||
"enums": [
|
||||
{
|
||||
"name": NameStylePython.toStr(name, "enum_name"),
|
||||
"consts": [
|
||||
(NameStylePython.toStr(k, "enum_item"), v)
|
||||
for k, v in e.values.items()
|
||||
]
|
||||
}
|
||||
for name, e in self.protocol.enums.items()
|
||||
],
|
||||
"bitfields": [
|
||||
{
|
||||
"name": NameStylePython.toStr(b.name, "class_name"),
|
||||
"size": b.get_size_bytes(),
|
||||
"bits": [
|
||||
{
|
||||
"name": NameStylePython.toStr(k, "class_member"),
|
||||
"pos": v
|
||||
}
|
||||
for k, v in b.bits.items()
|
||||
]
|
||||
}
|
||||
for name, b in self.protocol.bitfields.items()
|
||||
],
|
||||
"messages": [
|
||||
{
|
||||
"name": NameStylePython.toStr(name, "class_name"),
|
||||
"id": m.get_identifier(),
|
||||
"id_name": NameStylePython.toStr(ComponentName(["MSG", "ID"]) + name, "enum_item"),
|
||||
"size": m.get_size_bytes() + 1 + CRC_SIZE,
|
||||
"fields": [
|
||||
{
|
||||
"name": NameStylePython.toStr(k, "class_member"),
|
||||
"default_value": NameStylePython.toStr(v.bitfield.name, "class_name") + "()"
|
||||
if v.type == BprotoFieldBaseType.BITFIELD
|
||||
else (
|
||||
NameStylePython.toStr(v.enum.name, "enum_name") + "." + NameStylePython.toStr(list(v.enum.values.keys())[0], "enum_item")
|
||||
if v.type == BprotoFieldBaseType.ENUM
|
||||
else PYTHON_DATA_TYPE_MAP[v.type][1]
|
||||
),
|
||||
"size": v.get_size_bytes(),
|
||||
"array_size": v.array_size,
|
||||
"type": map_data_type_anotation(v),
|
||||
"to_bytes_conversion": map_to_bytes_conversion(v, ENDIANDNESS),
|
||||
"from_bytes_conversion": map_from_bytes_conversion(v, ENDIANDNESS)
|
||||
}
|
||||
for k, v in m.fields.items()
|
||||
]
|
||||
}
|
||||
for name, m, in self.protocol.messages.items()
|
||||
]
|
||||
}
|
||||
|
||||
return BackendFSOutFolder(self.output_folder, [
|
||||
BackendFSOutputJinjaFile( # Enum
|
||||
self.jinja_env,
|
||||
"python/template/enum.py.jinja2",
|
||||
f"{NameStylePython.toStr(self.protocol.name, 'enum_item')}_protocol_enum.py",
|
||||
self.jinja_context
|
||||
),
|
||||
BackendFSOutputJinjaFile( # Bitfields
|
||||
self.jinja_env,
|
||||
"python/template/bproto_protocol_bitfield.py.jinja2",
|
||||
f"{NameStylePython.toStr(self.protocol.name, 'enum_item')}_protocol_bitfield.py",
|
||||
self.jinja_context
|
||||
),
|
||||
BackendFSOutputJinjaFile( # Message IDs
|
||||
self.jinja_env,
|
||||
"python/template/bproto_protocol_packets_ids.py.jinja2",
|
||||
f"{NameStylePython.toStr(self.protocol.name, 'enum_item')}_protocol_message_ids.py",
|
||||
self.jinja_context
|
||||
),
|
||||
BackendFSOutputJinjaFile( # Message
|
||||
self.jinja_env,
|
||||
"python/template/bproto_protocol_packets.py.jinja2",
|
||||
f"{NameStylePython.toStr(self.protocol.name, 'enum_item')}_protocol_packets.py",
|
||||
self.jinja_context
|
||||
),
|
||||
BackendFSOutStaticConent(
|
||||
"template/python/static/bproto_base.py",
|
||||
"bproto_base.py"
|
||||
),
|
||||
BackendFSOutStaticConent(
|
||||
"template/python/static/bproto_error.py",
|
||||
"bproto_error.py"
|
||||
)
|
||||
])
|
||||
Reference in New Issue
Block a user