diff --git a/src/objdictgen/__main__.py b/src/objdictgen/__main__.py index ca22c8d..db9f01d 100644 --- a/src/objdictgen/__main__.py +++ b/src/objdictgen/__main__.py @@ -141,6 +141,7 @@ def main(debugopts: DebugOpts, args: Sequence[str]|None = None): help="Store in internal format (json only)") subp.add_argument('--no-sort', action="store_true", help="Don't order of parameters in output OD") + subp.add_argument('--no-can-festival', action='store_true', help="Generate a second set of files, modified to not include from canfestival") # -- DIFF -- subp = subparser.add_parser('diff', parents=[common_opts], help=""" @@ -263,7 +264,7 @@ def main(debugopts: DebugOpts, args: Sequence[str]|None = None): od.DumpFile(opts.out, filetype=opts.type, # These additional options are only used for JSON output - sort=not opts.no_sort, internal=opts.internal, validate=not opts.novalidate + sort=not opts.no_sort, internal=opts.internal, validate=not opts.novalidate, no_can_festival=opts.no_can_festival ) diff --git a/src/objdictgen/gen_cfile.py b/src/objdictgen/gen_cfile.py index 5dbb729..81e1fa0 100644 --- a/src/objdictgen/gen_cfile.py +++ b/src/objdictgen/gen_cfile.py @@ -21,6 +21,7 @@ import re from collections import UserDict +import copy from dataclasses import dataclass from pathlib import Path from typing import Any @@ -105,6 +106,11 @@ def __init__(self, *args, **kwargs): def __getattr__(self, name: str) -> Any: """Look up unknown attributes in the data dictionary.""" return self.data[name] + + def __copy__(self): + newInstance = CFileContext(self.data.copy()) + newInstance.internal_types = self.internal_types.copy() + return newInstance # FIXME: Delete this method when everything is converted to f-strings def text(self, s: str = "") -> Text: @@ -184,8 +190,30 @@ def compute_value(value: TODValue, ctype: str) -> tuple[str, str]: return f"-0x{-value:X}", f"\t/* {value} */" return f"0x{value:X}", f"\t/* {value} */" - -def generate_file_content(node: NodeProtocol, headerfile: str, pointers_dict=None) -> tuple[str, str, str]: +def convert_from_canopen_to_c_type(type): + # Used to convert types when ctype is an illegal value (e.g. valueRange_X) + type_map = {} + type_map["boolean"] = "bool" + type_map["int8"] = "int8_t" + type_map["int16"] = "int16_t" + type_map["int32"] = "int32_t" + type_map["int40"] = "int64_t" + type_map["int48"] = "int64_t" + type_map["int56"] = "int64_t" + type_map["int64"] = "int64_t" + type_map["uint8"] = "uint8_t" + type_map["uint16"] = "uint16_t" + type_map["uint32"] = "uint32_t" + type_map["uint40"] = "uint64_t" + type_map["uint48"] = "uint64_t" + type_map["uint56"] = "uint64_t" + type_map["uint64"] = "uint64_t" + type_map["real32"] = "float" + type_map["real64"] = "double" + return type_map.get(type, "char") + + +def generate_file_content(node: NodeProtocol, headerfile: str, no_can_festival: bool, pointers_dict=None) -> tuple[str, str, str, str, str]: """ pointers_dict = {(Idx,Sidx):"VariableName",...} """ @@ -277,8 +305,31 @@ def generate_file_content(node: NodeProtocol, headerfile: str, pointers_dict=Non mappedVariableContent = ctx.text() pointedVariableContent = ctx.text() strDeclareHeader = ctx.text() + noCanFestivalDeclarations = ctx.text() + noCanFestivalDefinitions = ctx.text() indexContents: dict[int, str|Text] = {} headerObjDefinitionContent = ctx.text() + + def add_file_content(header_content, c_content): + nonlocal strDeclareHeader + nonlocal mappedVariableContent + nonlocal noCanFestivalDeclarations + nonlocal noCanFestivalDefinitions + + strDeclareHeader %= header_content + mappedVariableContent %= c_content + + if "valueRange_" in typeinfos.ctype: + ctx["subIndexType"] = "uint8_t" + elif typeinfos.ctype == "visible_string": + ctx["subIndexType"] = "char" + else: + ctx["subIndexType"] = convert_from_canopen_to_c_type(typeinfos.ctype) + + noCanFestivalDeclarations %= header_content + noCanFestivalDefinitions %= c_content + ctx["subIndexType"] = typeinfos.type + for index in listindex: ctx["index"] = index entry_infos = node.GetEntryInfos(index) @@ -314,7 +365,16 @@ def generate_file_content(node: NodeProtocol, headerfile: str, pointers_dict=Non ctx["value"], ctx["comment"] = compute_value(values, typeinfos.ctype) if index in variablelist: ctx["name"] = RE_STARTS_WITH_DIGIT.sub(r'_\1', format_name(subentry_infos["name"])) - strDeclareHeader %= ( + add_file_content( + ( + "extern {subIndexType} {name}{suffix};" + "\t\t/* Mapped at index 0x{index:04X}, subindex 0x00*/\n" + ), + ( + "{subIndexType} {name}{suffix} = {value};" + "\t\t/* Mapped at index 0x{index:04X}, subindex 0x00 */\n" + )) + """strDeclareHeader %= ( "extern {subIndexType} {name}{suffix};" "\t\t/* Mapped at index 0x{index:04X}, subindex 0x00*/\n" ) @@ -322,6 +382,20 @@ def generate_file_content(node: NodeProtocol, headerfile: str, pointers_dict=Non "{subIndexType} {name}{suffix} = {value};" "\t\t/* Mapped at index 0x{index:04X}, subindex 0x00 */\n" ) + if "valueRange_" in typeinfos.ctype: + ctx["subIndexType"] = "uint8_t" + elif typeinfos.ctype == "visible_string": + ctx["subIndexType"] = "char" + else: + ctx["subIndexType"] = convert_from_canopen_to_c_type(typeinfos.ctype) + noCanFestivalDeclarations %= ( + "extern {subIndexType} {name}{suffix};" + "\t\t/* Mapped at index 0x{index:04X}, subindex 0x00*/\n" + ) + noCanFestivalDefinitions %= ( + "{subIndexType} {name}{suffix} = {value};" + "\t\t/* Mapped at index 0x{index:04X}, subindex 0x00 */\n" + )""" else: strindex %= ( " " @@ -356,7 +430,16 @@ def generate_file_content(node: NodeProtocol, headerfile: str, pointers_dict=Non if index in variablelist: ctx["name"] = RE_STARTS_WITH_DIGIT.sub(r'_\1', format_name(entry_infos["name"])) ctx["values_count"] = str(len(values) - 1) - strDeclareHeader %= ( + add_file_content( + ( + "extern {subIndexType} {name}[{values_count}]{suffix};\t\t" + "/* Mapped at index 0x{index:04X}, subindex 0x01 - 0x{length:02X} */\n" + ), + ( + "{subIndexType} {name}[]{suffix} =\t\t" + "/* Mapped at index 0x{index:04X}, subindex 0x01 - 0x{length:02X} */\n {{\n" + )) + """strDeclareHeader %= ( "extern {subIndexType} {name}[{values_count}]{suffix};\t\t" "/* Mapped at index 0x{index:04X}, subindex 0x01 - 0x{length:02X} */\n" ) @@ -364,6 +447,22 @@ def generate_file_content(node: NodeProtocol, headerfile: str, pointers_dict=Non "{subIndexType} {name}[]{suffix} =\t\t" "/* Mapped at index 0x{index:04X}, subindex 0x01 - 0x{length:02X} */\n {{\n" ) + if "valueRange_" in typeinfos.ctype: + ctx["subIndexType"] = "uint8_t" + elif typeinfos.ctype == "visible_string": + ctx["subIndexType"] = "char" + else: + ctx["subIndexType"] = convert_from_canopen_to_c_type(typeinfos.ctype) + noCanFestivalDeclarations %= ( + "extern {subIndexType} {name}[{values_count}]{suffix};\t\t" + "/* Mapped at index 0x{index:04X}, subindex 0x01 - 0x{length:02X} */\n" + ) + noCanFestivalDefinitions %= ( + "{subIndexType} {name}[]{suffix} =\t\t" + "/* Mapped at index 0x{index:04X}, subindex 0x01 - 0x{length:02X} */\n {{\n" + ) + ctx["subIndexType"] = typeinfos.type""" + for subindex, value in enumerate(values): sep = "," if subindex > 0: @@ -376,7 +475,9 @@ def generate_file_content(node: NodeProtocol, headerfile: str, pointers_dict=Non f"index: 0x{index:04X}, subindex: 0x{subindex:02X}" ) mappedVariableContent += f" {value}{sep}{comment}\n" + noCanFestivalDefinitions += f" {value}{sep}{comment}\n" mappedVariableContent += " };\n" + noCanFestivalDefinitions += " };\n" else: strindex %= ( " " @@ -416,7 +517,16 @@ def generate_file_content(node: NodeProtocol, headerfile: str, pointers_dict=Non ctx["value"], ctx["comment"] = compute_value(value, typeinfos.ctype) ctx["name"] = format_name(subentry_infos["name"]) if index in variablelist: - strDeclareHeader %= ( + add_file_content( + ( + "extern {subIndexType} {parent}_{name}{suffix};\t\t" + "/* Mapped at index 0x{index:04X}, subindex 0x{subindex:02X} */\n" + ), + ( + "{subIndexType} {parent}_{name}{suffix} = {value};\t\t" + "/* Mapped at index 0x{index:04X}, subindex 0x{subindex:02X} */\n" + )) + """strDeclareHeader %= ( "extern {subIndexType} {parent}_{name}{suffix};\t\t" "/* Mapped at index 0x{index:04X}, subindex 0x{subindex:02X} */\n" ) @@ -424,6 +534,20 @@ def generate_file_content(node: NodeProtocol, headerfile: str, pointers_dict=Non "{subIndexType} {parent}_{name}{suffix} = {value};\t\t" "/* Mapped at index 0x{index:04X}, subindex 0x{subindex:02X} */\n" ) + if "valueRange_" in typeinfos.ctype: + ctx["subIndexType"] = "uint8_t" + elif typeinfos.ctype == "visible_string": + ctx["subIndexType"] = "char" + else: + ctx["subIndexType"] = convert_from_canopen_to_c_type(typeinfos.ctype) + noCanFestivalDeclarations %= ( + "extern {subIndexType} {parent}_{name}{suffix};\t\t" + "/* Mapped at index 0x{index:04X}, subindex 0x{subindex:02X} */\n" + ) + noCanFestivalDefinitions %= ( + "{subIndexType} {parent}_{name}{suffix} = {value};\t\t" + "/* Mapped at index 0x{index:04X}, subindex 0x{subindex:02X} */\n" + )""" else: strindex %= ( " " @@ -667,6 +791,17 @@ def generate_file_content(node: NodeProtocol, headerfile: str, pointers_dict=Non # Write File Content # -------------------------------------------------------------------------- + noCanFestivalFileContent = ctx.text(FILE_HEADER) + noCanFestivalFileContent += f""" +#include "{headerfile.replace(".", "_no_can_festival.")}" +""" + noCanFestivalFileContent += """ +/**************************************************************************/ +/* Declaration of mapped variables */ +/**************************************************************************/ +""" + noCanFestivalFileContent += noCanFestivalDefinitions + fileContent = ctx.text(FILE_HEADER) fileContent += f""" #include "{headerfile}" @@ -766,6 +901,19 @@ def generate_file_content(node: NodeProtocol, headerfile: str, pointers_dict=Non # Write Header File Content # -------------------------------------------------------------------------- + ctx["file_include_name"] = headerfile.replace(".", "_no_can_festival.").replace(".", "_").upper() + noCanFestivalHeaderContent = ctx.text(FILE_HEADER) + noCanFestivalHeaderContent %= """ +#ifndef {file_include_name} +#define {file_include_name} + +#include + +""" + + noCanFestivalHeaderContent += noCanFestivalDeclarations + noCanFestivalHeaderContent %= "\n#endif // {file_include_name}\n" + ctx["file_include_name"] = headerfile.replace(".", "_").upper() headerFileContent = ctx.text(FILE_HEADER) headerFileContent %= """ @@ -807,30 +955,42 @@ def generate_file_content(node: NodeProtocol, headerfile: str, pointers_dict=Non #endif /* {file_include_objdef_name} */ """ - return str(fileContent), str(headerFileContent), str(headerObjectDefinitionContent) + return str(fileContent), str(headerFileContent), str(headerObjectDefinitionContent), str(noCanFestivalFileContent), str(noCanFestivalHeaderContent) # ------------------------------------------------------------------------------ # Main Function # ------------------------------------------------------------------------------ -def GenerateFile(filepath: TPath, node: NodeProtocol, pointers_dict=None): +def GenerateFile(filepath: TPath, node: NodeProtocol, no_can_festival, pointers_dict=None): """Main function to generate the C file from a object dictionary node.""" filepath = Path(filepath) headerpath = filepath.with_suffix(".h") headerdefspath = Path(headerpath.parent / (headerpath.stem + "_objectdefines.h")) - content, header, header_defs = generate_file_content( - node, headerpath.name, pointers_dict, + content, header, header_defs, noCanFestivalContent, noCanFestivalHeader = generate_file_content( + node, headerpath.name, no_can_festival, pointers_dict, ) # Write main .c contents with open(filepath, "w", encoding="utf-8") as f: f.write(content) + # Write main .c contents with canfestival types and includes omitted + if no_can_festival: + filepath = filepath.with_name(f"{filepath.stem}_no_can_festival{filepath.suffix}") + with open(filepath, "w", encoding="utf-8") as f: + f.write(noCanFestivalContent) + # Write header file with open(headerpath, "w", encoding="utf-8") as f: f.write(header) + # Write header file with canfestival types and includes omitted + if no_can_festival: + headerpath = headerpath.with_name(f"{headerpath.stem}_no_can_festival{headerpath.suffix}") + with open(headerpath, "w", encoding="utf-8") as f: + f.write(noCanFestivalHeader) + # Write object definitions header with open(headerdefspath, "w", encoding="utf-8") as f: f.write(header_defs) diff --git a/src/objdictgen/node.py b/src/objdictgen/node.py index 60b1ce2..833f80a 100644 --- a/src/objdictgen/node.py +++ b/src/objdictgen/node.py @@ -192,7 +192,7 @@ def LoadJson(contents: str, validate=True) -> Node: """ Import a new Node from a JSON string """ return jsonod.generate_node(contents, validate=validate) - def DumpFile(self, filepath: TPath, filetype: str|None = "jsonc", **kwargs): + def DumpFile(self, filepath: TPath, filetype: str|None = "jsonc", no_can_festival=False, **kwargs): """ Save node into file """ # Attempt to determine the filetype from the filepath @@ -227,7 +227,7 @@ def DumpFile(self, filepath: TPath, filetype: str|None = "jsonc", **kwargs): if filetype == 'c': log.debug("Writing C files '%s'", filepath) # Convert filepath to str because it might be used with legacy code - gen_cfile.GenerateFile(str(filepath), self) + gen_cfile.GenerateFile(str(filepath), self, no_can_festival) return raise ValueError("Unknown file suffix, unable to write file")