Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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 .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"rainbow-panda.panda",
"ms-python.python",
"ms-python.vscode-pylance",
"mechatroner.rainbow-csv"
"mechatroner.rainbow-csv",
"Serhioromano.vscode-st"
]
}
},
Expand Down
104 changes: 104 additions & 0 deletions ComplexParser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import os
from jinja2 import Environment, FileSystemLoader
from util import paths

STRUCT_TOKEN = "STRUCT"
TYPE_TOKEN = "TYPE"


class ComplexParser:

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

def __rewriteSTFile(self, lines, complex_vars):
"""
Rewrite the ST file with complex variables.
"""
template = Environment(loader=self.__loader).get_template(
"function_block.st.j2"
)
program_text = ""
for var in complex_vars:
program_text += f"{template.render(name=var[0], vars=var[1])}\n\n"
program_text += "".join(lines)
file_name = f"processed_{os.path.basename(self.__stFile)}"
with open(os.path.join(paths.AbsDir(self.__stFile), file_name), "w") as f:
f.write(program_text)

return program_text

def ParseSTFile(self, file):
"""
Parse ST file to extract complex variables.
"""

if not file:
raise Exception("ST file not valid. Please ")

self.__stFile = file

lines = []

with open(self.__stFile, "r") as f:
lines = f.readlines()

complex_vars = []
new_lines = []
parsing = False
type_declaration = False

for l in lines:
line = l.strip()

if line == TYPE_TOKEN:
type_declaration = True
if f": {STRUCT_TOKEN}" in line:
if parsing:
raise Exception(
f"Error: Nested {STRUCT_TOKEN} declaration found in ST file."
)

if not type_declaration:
raise Exception(
f"Error: {STRUCT_TOKEN} found out of {TYPE_TOKEN} block declaration."
)
name = f"{line.split(':')[0].strip()}"
complex_vars.append((name, {}))
parsing = True
continue
elif f"END_{STRUCT_TOKEN}" in line:
parsing = False
continue
elif parsing:
splitted_line = line.replace(":=", ":").split(":")
if len(splitted_line) == 3:
# Handle complex variable with type and initial value
name, type, value = (
splitted_line[0].strip(),
splitted_line[1].strip(),
splitted_line[2].replace(";", "").strip(),
)
complex_vars[-1][1][name] = {"type": type, "value": value}
continue
name, type = (
splitted_line[0].strip(),
splitted_line[1].replace(";", "").strip(),
)
complex_vars[-1][1][name] = {"type": type, "value": None}
continue
# Remove type block if empty
elif f"END_{TYPE_TOKEN}" in line:
type_declaration = False
if TYPE_TOKEN in new_lines[-1]:
new_lines.pop()
continue
# Skip consecutive empty lines
elif not l.strip() and not (new_lines and new_lines[-1].strip()):
continue

new_lines.append(l)

return self.__rewriteSTFile(new_lines, complex_vars)
10 changes: 5 additions & 5 deletions ProjectController.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os
import traceback
import traceback, os
from jinja2 import Environment, FileSystemLoader
from runtime.typemapping import DebugTypesSize
import util.paths as paths
Expand All @@ -8,7 +7,9 @@
class ProjectController:

def __init__(self):

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

def SetCSVFile(self, filename):
Expand Down Expand Up @@ -160,8 +161,7 @@ def Generate_plc_debug_cvars(self):
def Generate_embedded_plc_debugger(self):
dvars, externs, enums = self.Generate_plc_debug_cvars()

loader = FileSystemLoader(os.path.join(paths.AbsDir(__file__)))
template = Environment(loader=loader).get_template("debug.c.j2")
template = Environment(loader=self.__loader).get_template("debug.c.j2")
cfile = os.path.join(paths.AbsDir(self._csvfile), "debug.c")
debug_text = template.render(
debug={
Expand Down
File renamed without changes.
16 changes: 16 additions & 0 deletions templates/function_block.st.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FUNCTION_BLOCK {{name}}
VAR_INPUT
{%-for k,v in vars.items()%}
{%- if v.value is not none %}
{{k}} : {{v.type}} := {{v.value}};
{%- else %}
{{k}} : {{v.type}};
{%- endif %}
{%- endfor %}
END_VAR
VAR
local_temp : DINT;
END_VAR

local_temp := 0;
END_FUNCTION_BLOCK
35 changes: 29 additions & 6 deletions xml2st.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import PLCGenerator
from PLCControler import PLCControler
from ProjectController import ProjectController
from ComplexParser import ComplexParser


def compile_xml_to_st(xml_file_path):
Expand Down Expand Up @@ -56,6 +57,20 @@ def compile_xml_to_st(xml_file_path):
sys.exit(1)


def parse_complex_variables(st_file):
if not os.path.isfile(st_file) or not st_file.lower().endswith(".st"):
print(f"Error: Invalid file '{st_file}'. A path to a st file is expected.")
return None

parser = ComplexParser()
try:
complex_vars = parser.ParseSTFile(st_file)
return complex_vars
except Exception as e:
print(f"Error parsing ST file: {e}", file=sys.stderr)
sys.exit(1)


def generate_debugger_file(csv_file):
if not os.path.isfile(csv_file) or not csv_file.lower().endswith(".csv"):
print(f"Error: Invalid file '{csv_file}'. A path to a csv file is expected.")
Expand Down Expand Up @@ -101,6 +116,14 @@ def main():

print("Saving ST file...")

st_file = os.path.abspath(args.generate_st).replace("plc.xml", "program.st")
with open(st_file, "w") as file:
file.write(program_text)

print("Parsing complex variables...")

parse_complex_variables(st_file)

except Exception as e:
print(f"Error generating ST file: {e}", file=sys.stderr)
sys.exit(1)
Expand All @@ -119,6 +142,12 @@ def main():

print("Saving files...")

st_file = os.path.abspath(args.generate_debug[0]).replace(
"plc.xml", "program.st"
)
with open(st_file, "w") as file:
file.write(program_text)

except Exception as e:
print(f"Error generating debug: {e}", file=sys.stderr)
sys.exit(1)
Expand All @@ -127,12 +156,6 @@ def main():
print("Error: No valid arguments provided. Use --help for usage information.")
return

st_file = os.path.abspath(args.generate_st or args.generate_debug[0]).replace(
"plc.xml", "program.st"
)
with open(st_file, "w") as file:
file.write(program_text)


if __name__ == "__main__":
main()