Обновлены вспомогательные файлы и скрипты

This commit is contained in:
2026-08-28 15:20:57 +03:00
parent 297dc62f48
commit edba921ffa
4 changed files with 980 additions and 42 deletions
+133 -41
View File
@@ -45,18 +45,21 @@ END_KEYWORDS = {
"ACTION": "END_ACTION",
}
IMPL_MARKER = "(*----- IMPLEMENTATION -----*)"
# Терпимо к вариациям пробелов/кол-ва дефисов, которые может внести
# автоформатирование редактора, напр. "(* ----- IMPLEMENTATION ----- *)"
# вместо канонического "(*----- IMPLEMENTATION -----*)".
IMPL_MARKER_RE = re.compile(r"\(\*\s*-{3,}\s*IMPLEMENTATION\s*-{3,}\s*\*\)")
HEADER_RE = re.compile(
r"^\(\*\n"
r"=+\n"
r" Project\s*: (?P<project>[^\n]*)\n"
r" Device\s*: (?P<device>[^\n]*)\n"
r" Path\s*: (?P<path>[^\n]*)\n"
r" Name\s*: (?P<name>[^\n]*)\n"
r" Type\s*: (?P<type>[^\n]*)\n"
r"=+\n"
r"\*\)\n",
r"^\(\*[ \t]*\n"
r"=+[ \t]*\n"
r"[ \t]*Project[ \t]*:[ \t]*(?P<project>[^\n]*?)[ \t]*\n"
r"[ \t]*Device[ \t]*:[ \t]*(?P<device>[^\n]*?)[ \t]*\n"
r"[ \t]*Path[ \t]*:[ \t]*(?P<path>[^\n]*?)[ \t]*\n"
r"[ \t]*Name[ \t]*:[ \t]*(?P<name>[^\n]*?)[ \t]*\n"
r"[ \t]*Type[ \t]*:[ \t]*(?P<type>[^\n]*?)[ \t]*\n"
r"=+[ \t]*\n"
r"[ \t]*\*\)[ \t]*\n",
re.MULTILINE,
)
@@ -216,8 +219,9 @@ def parse_block_text(body, type_label):
impl = parts[1].strip("\n") if len(parts) > 1 else ""
return None, (to_crlf(impl) + "\r\n" if impl else "")
if IMPL_MARKER in body:
decl, impl = body.split(IMPL_MARKER, 1)
m = IMPL_MARKER_RE.search(body)
if m:
decl, impl = body[:m.start()], body[m.end():]
decl = decl.strip("\n")
impl = impl.strip("\n")
return (to_crlf(decl) + "\r\n" if decl else ""), (to_crlf(impl) + "\r\n" if impl else "")
@@ -227,6 +231,41 @@ def parse_block_text(body, type_label):
return (to_crlf(decl) + "\r\n" if decl else ""), None
def extract_return_type(declaration, type_label, object_name):
"""Извлекает return type из FUNCTION/METHOD/PROPERTY declaration."""
if not declaration:
return None
text = normalize_newlines(declaration)
# Удаляем только ведущие прагмы для анализа первой строки.
while True:
new_text = re.sub(r"^\s*\{[^}]*\}\s*", "", text, count=1)
if new_text == text:
break
text = new_text
for line in text.split("\n"):
line = line.strip()
if not line:
continue
# Для анализа нам не нужны inline block-comments.
line = re.sub(r"\(\*.*?\*\)", "", line).strip()
m = re.match(
r"^" + re.escape(type_label) + r"\s+\S+\s*:\s*(.+?)\s*$",
line,
re.IGNORECASE
)
if m:
return m.group(1).strip()
# Если это нужный заголовок, но return type отсутствует.
if line.upper().startswith(type_label + " "):
return None
return None
# ---------- запись в объекты проекта ----------
def set_declaration(obj, text):
@@ -247,63 +286,116 @@ def set_implementation(obj, text):
print(" ! ошибка записи implementation: %s" % e)
def get_or_create_pou(parent, name, type_label):
def get_or_create_pou(parent, name, type_label, declaration_text):
"""Создаёт PROGRAM/FB/FUNCTION/INTERFACE через специализированный API."""
existing = find_child(parent, name)
if existing is not None:
print(" = %s '%s' уже существует" % (type_label, name))
return existing, False
try:
if type_label == "INTERFACE":
obj = parent.create_interface(name)
else:
pou_kind = {
"PROGRAM": "Program",
"FUNCTION_BLOCK": "FunctionBlock",
"FUNCTION": "Function",
}[type_label]
if type_label == "PROGRAM":
create_program = getattr(parent, "create_program", None)
if create_program is not None:
return create_program(name, ImplementationLanguages.st), True
return parent.create_pou(name, PouType.Program, ImplementationLanguages.st), True
if type_label == "FUNCTION_BLOCK":
create_fb = getattr(parent, "create_function_block", None)
if create_fb is not None:
return create_fb(name, ImplementationLanguages.st), True
return parent.create_pou(name, PouType.FunctionBlock, ImplementationLanguages.st), True
if type_label == "FUNCTION":
return_type = extract_return_type(declaration_text, "FUNCTION", name)
if not return_type:
raise RuntimeError("не удалось определить return type FUNCTION '%s'" % name)
print(" -> FUNCTION '%s' : %s" % (name, return_type))
create_function = getattr(parent, "create_function", None)
if create_function is not None:
return create_function(name, return_type, ImplementationLanguages.st), True
# Fallback для старых версий API.
try:
pou_type_enum = getattr(PouType, pou_kind) # noqa: F821 (глобал CODESYS)
except Exception:
pou_type_enum = pou_kind
obj = parent.create_pou(name, pou_type_enum, ImplementationLanguages.st) # noqa: F821
return obj, True
return parent.create_pou(name, PouType.Function, return_type, ImplementationLanguages.st), True
except TypeError:
return parent.create_pou(name, PouType.Function, ImplementationLanguages.st, return_type), True
if type_label == "INTERFACE":
return parent.create_interface(name), True
raise RuntimeError("неизвестный тип POU '%s'" % type_label)
except Exception as e:
print(" ! не удалось создать %s '%s': %s" % (type_label, name, e))
print(" ! НЕ УДАЛОСЬ создать %s '%s': %s" % (type_label, name, e))
return None, False
def get_or_create_method(parent_pou, name):
def get_or_create_method(parent_pou, name, declaration_text):
existing = find_child(parent_pou, name)
if existing is not None:
print(" = METHOD '%s' уже существует" % name)
return existing, False
return_type = extract_return_type(declaration_text, "METHOD", name)
try:
obj = parent_pou.create_method(name, "", "", ImplementationLanguages.st) # noqa: F821
return obj, True
return parent_pou.create_method(
name, return_type, ImplementationLanguages.st
), True
except TypeError:
try:
return parent_pou.create_method(name, return_type), True
except Exception as e:
print(" ! НЕ УДАЛОСЬ создать METHOD '%s': %s" % (name, e))
return None, False
except Exception as e:
print(" ! не удалось создать METHOD '%s': %s" % (name, e))
print(" ! НЕ УДАЛОСЬ создать METHOD '%s': %s" % (name, e))
return None, False
def get_or_create_property(parent_pou, name):
def get_or_create_property(parent_pou, name, declaration_text):
existing = find_child(parent_pou, name)
if existing is not None:
print(" = PROPERTY '%s' уже существует" % name)
return existing, False
return_type = extract_return_type(declaration_text, "PROPERTY", name)
if not return_type:
print(" ! НЕ УДАЛОСЬ определить return type PROPERTY '%s'" % name)
return None, False
try:
obj = parent_pou.create_property(name, "", ImplementationLanguages.st) # noqa: F821
return obj, True
return parent_pou.create_property(
name, return_type, ImplementationLanguages.st
), True
except TypeError:
try:
return parent_pou.create_property(name, return_type), True
except Exception as e:
print(" ! НЕ УДАЛОСЬ создать PROPERTY '%s': %s" % (name, e))
return None, False
except Exception as e:
print(" ! не удалось создать PROPERTY '%s': %s" % (name, e))
print(" ! НЕ УДАЛОСЬ создать PROPERTY '%s': %s" % (name, e))
return None, False
def get_or_create_action(parent_pou, name):
existing = find_child(parent_pou, name)
if existing is not None:
print(" = ACTION '%s' уже существует" % name)
return existing, False
try:
obj = parent_pou.create_action(name, ImplementationLanguages.st) # noqa: F821
return obj, True
return parent_pou.create_action(name, ImplementationLanguages.st), True
except TypeError:
try:
return parent_pou.create_action(name), True
except Exception as e:
print(" ! НЕ УДАЛОСЬ создать ACTION '%s': %s" % (name, e))
return None, False
except Exception as e:
print(" ! не удалось создать ACTION '%s': %s" % (name, e))
print(" ! НЕ УДАЛОСЬ создать ACTION '%s': %s" % (name, e))
return None, False
@@ -374,7 +466,7 @@ def process_st_file(proj, filepath, dry_run):
continue
if type_label in POU_TYPES:
obj, _ = get_or_create_pou(target_parent, name, type_label)
obj, _ = get_or_create_pou(target_parent, name, type_label, decl_text)
if obj is None:
parent_pou = None
continue
@@ -387,9 +479,9 @@ def process_st_file(proj, filepath, dry_run):
print(" ! нет родительского POU в этом файле, пропуск")
continue
if type_label == "METHOD":
obj, _ = get_or_create_method(parent_pou, name)
obj, _ = get_or_create_method(parent_pou, name, decl_text)
else:
obj, _ = get_or_create_property(parent_pou, name)
obj, _ = get_or_create_property(parent_pou, name, decl_text)
if obj is None:
continue
set_declaration(obj, decl_text)