Skip to content
Snippets Groups Projects

Add validation rules for silecs design deploy files

Merged m.nabywaniec requested to merge 61-validation-rules into master
1 file
+ 155
0
Compare changes
  • Side-by-side
  • Inline
+ 155
0
@@ -9,6 +9,8 @@ import subprocess
import lxml.etree
from xml.dom import minidom
from packaging import version
SILECS_VERSION = "2.3.0"
SILECSDESIGN = "silecsdesign"
SILECSDEPLOY = "silecsdeploy"
@@ -149,8 +151,161 @@ def create_backup_file(filepath):
with open(backup_file, 'w+'):
shutil.copyfile(src=filepath, dst=backup_file)
def get_version_underscored_tiny_as_x(version):
"""Get underscored version string with tiny part as x
eg. 2.3.4 -> 2_3_x"""
version_splitted = version.split(".")
major = version_splitted[0]
minor = version_splitted[1]
return major + "_" + minor + "_x"
def get_silecs_version_from_silecsdesign_file(silecsdesign_file):
silecs_design_document = minidom.parse(silecsdesign_file)
silecs_design_element = silecs_design_document.getElementsByTagName("SILECS-Design")[0]
version = silecs_design_element.getAttribute("silecs-version")
return version
def get_silecs_version_from_silecsdeploy_file(silecsdeploy_file):
silecs_deploy_document = minidom.parse(silecsdeploy_file)
silecs_deploy_element = silecs_deploy_document.getElementsByTagName("SILECS-Depoloy")[0]
version = silecs_deploy_element.getAttribute("silecs-version")
return version
def get_silecs_version_from_file(filepath):
if is_silecs_design_file(filepath):
return get_silecs_version_from_silecsdesign_file(filepath)
else:
return get_silecs_version_from_silecsdeploy_file(filepath)
def is_silecs_design_file(filepath):
return get_extension(filepath) == SILECSDESIGN
def is_silecs_deploy_file(filepath):
return get_extension(filepath) == SILECSDEPLOY
def get_x_path(node):
"""Get XPath for node"""
parent = node.parentNode
if not parent:
return "/"
else:
return get_x_path(parent) + "/" + node.nodeName + "[@name='"+ node.getAttribute("name") + "']"
##### Validate silecs design deploy ######
def class_rule_register_dim2(document):
errors = []
try:
register_nodes = document.getElementsByTagName("Setting-Block")
for register in register_nodes:
print(register.getElementsByTagName("Setting-Register"))
if register.hasAttribute("array-dim2") and not register.hasAttribute("array-dim1"):
errors.append(f"array-dim1 is required if array-dim2 is defined {get_x_path(register)}")
except Exception as e:
errors.append(str(e))
return errors
def class_rule_string_length(document):
errors = []
try:
register_nodes = document.getElementsByTagName("Register")
for register in register_nodes:
if not register.getAttribute("format") != "string":
if not register.hasAttribute("string-len"):
errors.append(f"string-len attribute only allowed for @format='string' {get_x_path(register)}")
except Exception as e:
errors.append(str(e))
return errors
def class_rule_RO_Slave(document):
errors = []
try:
block_nodes = document.getElementsByTagName("Block")
for block in block_nodes:
if block.getAttribute("mode") == "READ-ONLY":
register_nodes = block.getElementsByTagName("Register")
for register in register_nodes:
if not register.getAttribute("synchro") == "SLAVE":
errors.append(f"Register cannot have SLAVE synchro attribute within READ-ONLY block' {get_x_path(register)}")
except Exception as e:
errors.append(str(e))
return errors
def class_rule_WO_Master(document):
errors = []
try:
block_nodes = document.getElementsByTagName("Block")
for block in block_nodes:
if block.getAttribute("mode") == "WRITE-ONLY":
register_nodes = block.getElementsByTagName("Register")
for register in register_nodes:
if not register.getAttribute("synchro") == "MASTER":
errors.append(f"Register cannot have SLAVE synchro attribute within READ-ONLY block' {get_x_path(register)}")
except Exception as e:
errors.append(str(e))
return errors
def deploy_rule_addressing_SchneiderM340(document):
errors = []
try:
schneider_plc_nodes = document.getElementsByTagName("Schneider-PLC")
for plc in schneider_plc_nodes:
if plc.getAttribute("model") == "M340":
base_address = int(plc.getAttribute("base-address"))
if base_address % 2 != 0:
errors.append("Only even addressing is allowed for UNITY_M340. Please change the base address value to an even number")
except Exception as e:
errors.append(str(e))
return errors
def deploy_rule_addressing_BeckhoffCX90xx(document):
errors = []
try:
beckhoff_plc_nodes = document.getElementsByTagName("Beckhoff-PLC")
for plc in beckhoff_plc_nodes:
if plc.getAttribute("model") == "CX9020":
base_address = int(plc.getAttribute("base-address"))
if base_address % 2 != 0:
errors.append("Only even addressing is allowed for TWINCAT_CX9020. Please change the base address value to an even number")
except Exception as e:
errors.append(str(e))
return errors
def validate_internal_rules(silecs_design_deploy_path):
"""Internal validation rules for silecs design deploy files"""
silecs_document = minidom.parse(silecs_design_deploy_path)
errors = []
silecs_version = get_silecs_version_from_file(silecs_design_deploy_path)
if is_silecs_design_file(silecs_design_deploy_path):
if version.parse(silecs_version) < version.parse("2.0.0"):
errors.extend(class_rule_register_dim2(silecs_document))
errors.extend(class_rule_string_length(silecs_document))
errors.extend(class_rule_RO_Slave(silecs_document))
errors.extend(class_rule_WO_Master(silecs_document))
elif is_silecs_deploy_file(silecs_design_deploy_path):
errors.extend(deploy_rule_addressing_SchneiderM340(silecs_document))
errors.extend(deployRule_addressing_BeckhoffCX90xx(silecs_document))
is_valid = not errors
return is_valid, errors
def validate(silecs_design_deploy_path, xsd_path):
"""Validate silecs design deploy"""
try:
Loading