Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/objdictgen/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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="""
Expand Down Expand Up @@ -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
)


Expand Down
178 changes: 169 additions & 9 deletions src/objdictgen/gen_cfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import re
from collections import UserDict
import copy
from dataclasses import dataclass
from pathlib import Path
from typing import Any
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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",...}
"""
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -314,14 +365,37 @@ 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"
)
mappedVariableContent %= (
"{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"
Comment on lines +387 to +388

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not go as far as to say that visible_string is a char. It should either be char* (which probably would cause issues in other places?) or the tool should ignore it with an error (I don't think there are those in the callback right now, are there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the global object which are of type_visible string are defined with a char array and not char*

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I will just assume you tested it somehow, as I still don't follow how char is the same as 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 %= (
" "
Expand Down Expand Up @@ -356,14 +430,39 @@ 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"
)
mappedVariableContent %= (
"{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:
Expand All @@ -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 %= (
" "
Expand Down Expand Up @@ -416,14 +517,37 @@ 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"
)
mappedVariableContent %= (
"{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 %= (
" "
Expand Down Expand Up @@ -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}"
Expand Down Expand Up @@ -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 <cstdint>

"""

noCanFestivalHeaderContent += noCanFestivalDeclarations
noCanFestivalHeaderContent %= "\n#endif // {file_include_name}\n"

ctx["file_include_name"] = headerfile.replace(".", "_").upper()
headerFileContent = ctx.text(FILE_HEADER)
headerFileContent %= """
Expand Down Expand Up @@ -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)
4 changes: 2 additions & 2 deletions src/objdictgen/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down
Loading