Обновлены вспомогательные файлы и скрипты
This commit is contained in:
+133
-41
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user