Skip to content
Merged
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
101 changes: 60 additions & 41 deletions ComplexParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,9 @@ def __init__(self):
self.structs = []
self.programs = []
self.csv_vars = []
self.array_dependant = []
self.array_dependant_names = []
self.simple_types = []
self.simple_types_names = []
self.complex_types = []
self.complex_structs = []
self.function_blocks = []
self.__loader = FileSystemLoader(
Expand All @@ -153,8 +154,9 @@ def __clear(self):
self.structs = []
self.programs = []
self.csv_vars = []
self.array_dependant = []
self.array_dependant_names = []
self.simple_types = []
self.simple_types_names = []
self.complex_types = []
self.complex_structs = []
self.function_blocks = []

Expand Down Expand Up @@ -220,7 +222,7 @@ def __getBlockLines(self, block, ignoreComplexStructs=True):
if (
ignoreComplexStructs
and isinstance(block, _StructInstance)
and block.name not in self.array_dependant_names
and block.name in self.complex_types
):
return []
for line in block.lines:
Expand All @@ -231,45 +233,49 @@ def __getBlockLines(self, block, ignoreComplexStructs=True):

return lines

def __isBlockAnArrayType(self, block):
for a in self.arrays:
if block.name == a.data_type:
return True
return False

def __analyseTypes(self, block):
"""
Separate structs that have to be manually parsed
Separate custom defined types into simple and complex types.
"""
changes = [b for b in block.inner_blocks if b.simple]
self.simple_types.extend(changes)

changes = [b for b in block.inner_blocks if self.__isBlockAnArrayType(b)]

for c in changes:
self.__checkSubtypes(c, block.inner_blocks)

self.array_dependant_names = list(set(self.array_dependant_names))
self.simple_types_names = [s.name for s in self.simple_types]

complex_blocks = [
b for b in block.inner_blocks if b.name not in self.array_dependant_names
]
self.complex_structs = [
b for b in complex_blocks if isinstance(b, _StructInstance)
]
complex_blocks = [b for b in block.inner_blocks if not b.simple]

def __checkSubtypes(self, subtype, blocks):
if isinstance(subtype, _StructInstance):
self.array_dependant.append(subtype)
self.array_dependant_names.append(subtype.name)
for inner_block in subtype.inner_blocks:
block = self.__findBlock(inner_block, blocks)
if block:
self.__checkSubtypes(block, blocks)

def __findBlock(self, block, block_list):
for b in block_list:
if b.name == block.data_type:
return b
return None
while changes:
changes = False
complex_blocks_copy = complex_blocks.copy()
complex_blocks = []
for complex_block in complex_blocks_copy:
if isinstance(complex_block, _VariableInstance):
complex_block.simple = (
complex_block.data_type in self.simple_types_names
)
if complex_block.simple:
changes = True
self.simple_types.append(complex_block)
self.simple_types_names.append(complex_block.name)
else:
complex_blocks.append(complex_block)
elif isinstance(complex_block, _StructInstance):
for inner_block in [
i for i in complex_block.inner_blocks if not i.simple
]:
if inner_block.data_type not in self.simple_types_names:
complex_blocks.append(complex_block)
break
else:
complex_block.simple = True
self.simple_types.append(complex_block)
self.simple_types_names.append(complex_block.name)
changes = True

self.complex_types.extend([b.name for b in complex_blocks])
self.complex_structs.extend(
[b for b in complex_blocks if isinstance(b, _StructInstance)]
)

def __separateOuterBlocks(self):
"""
Expand Down Expand Up @@ -314,10 +320,23 @@ def __getSTLines(self):
for inner_block in block.inner_blocks:
if (
not isinstance(inner_block, _StructInstance)
or inner_block.name in self.array_dependant_names
or inner_block.name in self.simple_types_names
):
break
else:
if (
len(
list(
filter(
lambda x: not isinstance(x, _InsertLine)
and not EMPTY_LINE.match(x),
block.lines,
)
)
)
> 2
):
lines.extend(self.__getBlockLines(block))
lines.append(self.__rewriteStructsAsFunctionBlocks())
continue
lines.extend(self.__getBlockLines(block))
Expand Down Expand Up @@ -351,7 +370,7 @@ def __getCustomType(self, type_name):
"""
Get the complex type by its name.
"""
for custom_type in self.array_dependant:
for custom_type in self.simple_types:
if custom_type.name == type_name:
return custom_type
return None
Expand Down Expand Up @@ -393,7 +412,7 @@ def __spreadDeclarations(
prefix = f"{prefix}.{block.name.upper()}"
if block.data_type in BASE_TYPES and write_base_types:
self.csv_vars.append({"name": prefix, "type": block.data_type})
elif block.data_type in self.array_dependant_names:
elif block.data_type in self.simple_types_names:
type = self.__getCustomType(block.data_type)
if type:
self.__spreadDeclarations(
Expand Down
47 changes: 27 additions & 20 deletions GlueGenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,19 @@
# __LOCATED_VAR(INT,__QW0,Q,W,0)
# __LOCATED_VAR(BOOL,__QX0_1,Q,X,0,1)


class GlueGenerator:

def __init__(self):
self.__loader = FileSystemLoader(os.path.join(paths.AbsDir(__file__), "templates"))
self.__loader = FileSystemLoader(
os.path.join(paths.AbsDir(__file__), "templates")
)

def __glue_logic(self, varName):
"""
Generate glue logic based on variable type.
"""

# Extract indices
print(f"Linking variable {varName}")
try:
Expand All @@ -28,38 +31,38 @@ def __glue_logic(self, varName):
raise Exception(f"Error parsing variable name '{varName}': {e}")

kind = varName[2] # I, Q, M
sub = varName[3] # X, B, W, D, L
sub = varName[3] # X, B, W, D, L

if kind == 'I':
if sub == 'X':
if kind == "I":
if sub == "X":
return f"bool_input_ptr[{pos1}][{pos2}] = (IEC_BOOL *){varName};"
elif sub == 'B':
elif sub == "B":
return f"byte_input_ptr[{pos1}] = (IEC_BYTE *){varName};"
elif sub == 'W':
elif sub == "W":
return f"int_input_ptr[{pos1}] = (IEC_UINT *){varName};"
elif sub == 'D':
elif sub == "D":
return f"dint_input_ptr[{pos1}] = (IEC_UDINT *){varName};"
elif sub == 'L':
elif sub == "L":
return f"lint_input_ptr[{pos1}] = (IEC_ULINT *){varName};"

elif kind == 'Q':
if sub == 'X':
elif kind == "Q":
if sub == "X":
return f"bool_output_ptr[{pos1}][{pos2}] = (IEC_BOOL *){varName};"
elif sub == 'B':
elif sub == "B":
return f"byte_output_ptr[{pos1}] = (IEC_BYTE *){varName};"
elif sub == 'W':
elif sub == "W":
return f"int_output_ptr[{pos1}] = (IEC_UINT *){varName};"
elif sub == 'D':
elif sub == "D":
return f"dint_output_ptr[{pos1}] = (IEC_UDINT *){varName};"
elif sub == 'L':
elif sub == "L":
return f"lint_output_ptr[{pos1}] = (IEC_ULINT *){varName};"

elif kind == 'M':
if sub == 'W':
elif kind == "M":
if sub == "W":
return f"int_memory_ptr[{pos1}] = (IEC_UINT *){varName};"
elif sub == 'D':
elif sub == "D":
return f"dint_memory_ptr[{pos1}] = (IEC_UDINT *){varName};"
elif sub == 'L':
elif sub == "L":
return f"lint_memory_ptr[{pos1}] = (IEC_ULINT *){varName};"

raise Exception(f"Unhandled variable type: {varName}")
Expand All @@ -74,7 +77,11 @@ def __parse_line(self, line):
print(f"Warning: Line '{line.strip()}' does not match expected format.")
return None
varType, varName = m.group(1), m.group(2)
return {"type": varType, "name": varName, "glue_code": self.__glue_logic(varName)}
return {
"type": varType,
"name": varName,
"glue_code": self.__glue_logic(varName),
}

def generate_glue_variables(self, located_vars_lines):
"""
Expand Down
23 changes: 22 additions & 1 deletion STParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"BYTE",
"WORD",
"DWORD",
"LWORD"
"LWORD",
]


Expand Down Expand Up @@ -137,8 +137,27 @@ def GetInfo(self, line):
return None


class _Function(_NamedBlock):
def __init__(self, name="function"):
super().__init__(name)
self.start = re.compile(
rf"^{self.name}\s+(?P<name>[A-Za-z_][A-Za-z0-9_]*)\s*:\s*(?P<return_type>[A-Za-z_][A-Za-z0-9_]*)\s*$"
)

def GetInfo(self, line):
match = self.start.match(line)
if match:
return {
"name": match.group("name"),
"type": self.name,
"return_type": match.group("return_type"),
}
return None

Comment thread
dcoutinho1328 marked this conversation as resolved.

TYPE = _Block("type")
FUNCTION_BLOCK = _NamedBlock("function_block")
FUNCTION = _Function()
PROGRAM = _NamedBlock("program")
CONFIGURATION = _NamedBlock("configuration")
RESOURCE = _NamedBlock("resource")
Expand All @@ -159,6 +178,7 @@ def GetInfo(self, line):
STRUCT,
ARRAY,
VARIABLE,
FUNCTION,
]

CLOSABLE_BLOCKS = [
Expand All @@ -168,4 +188,5 @@ def GetInfo(self, line):
CONFIGURATION,
RESOURCE,
STRUCT,
FUNCTION,
]
21 changes: 11 additions & 10 deletions xml2st.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,17 @@ def append_debugger_to_st(st_file, debug_text):
f.write("\n")
f.write(c_debug)


def generate_gluevars(located_vars_file):
if not os.path.isfile(located_vars_file) or not located_vars_file.lower().endswith(".h"):
if not os.path.isfile(located_vars_file) or not located_vars_file.lower().endswith(
".h"
):
print(
f"Error: Invalid file '{located_vars_file}'. A path to a LOCATED_VARIABLES.h file is expected.",
file=sys.stderr,
)
return None

# Read the LOCATED_VARIABLES.h file
with open(located_vars_file, "r") as f:
located_vars = f.readlines()
Expand All @@ -109,15 +112,13 @@ def generate_gluevars(located_vars_file):
# Print success message
print(f"Glue variables saved to {glue_vars_file}")


def main():
parser = argparse.ArgumentParser(
description="Process a PLCopen XML file and transpiles it into a Structured Text (ST) program."
)
parser.add_argument(
"--generate-st",
metavar=("XML_FILE"),
type=str,
help="The path to the XML file"
"--generate-st", metavar=("XML_FILE"), type=str, help="The path to the XML file"
)
parser.add_argument(
"--generate-debug",
Expand All @@ -127,10 +128,10 @@ def main():
help="Paths to the ST file and the variables CSV file",
)
parser.add_argument(
"--generate-gluevars",
metavar=("LOCATED_VARS_FILE"),
type=str,
help="The path to the LOCATED_VARIABLES.h file"
"--generate-gluevars",
metavar=("LOCATED_VARS_FILE"),
type=str,
help="The path to the LOCATED_VARIABLES.h file",
)

args = parser.parse_args()
Expand Down