Init Commit: Moved bproto to seperate repo

This commit is contained in:
AlexanderHD27
2025-04-14 14:43:03 +02:00
commit 45bfc724fc
125 changed files with 10822 additions and 0 deletions

View 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"
)
])