Skip to content
Snippets Groups Projects
Commit e148f4a1 authored by Nabywaniec's avatar Nabywaniec Committed by Mateusz Nabywaniec
Browse files

Reorganise Silecs CLI

Find all available versions

Add migrate project

Uncomment import library and multiple silecs files check

Create class SilecsCli to  dynamically import modules according to silecs version

Move util functions and constants from silecs cli

Fix installation script and paths in silecs env

Fix install script

Fix checnking file in diagnostic tool

Fix checking file in diagnostic tool

Fix opening diagnostic tool

Fix generating plc code

Fix generate code for silecs design

Add getting right underscored version and project type enum

Fix loading modules according to silecs version in file

Fix validate during generation

Fix typo when validate silecs deploy

Update tests

Fix missing self in method

Fix finding silecs file for migration

Fix importing migration modules

Rename invalid variable

Fix finding right xml schema

Apply release feature to new structure and fix imports

Fix problems with release param, update const and utils

Remove unused code

Fix versions during migration

Actualise validation procedure

Fix missing variabel in validation

Fix create backup during generation

Update tests

Move get templates to silecs utils

Fix proble with getting design template

Move help methods to utils

Get silecs version using methond from utils

Remove invalid import

Remove self in utils

Dynamically import codegen based on fesa

Use valid silecs env

Add info about release param directory to help
parent 04f569c7
No related branches found
No related tags found
No related merge requests found
......@@ -128,6 +128,10 @@ class MigrationBase(object):
context.saveTo(fd)
def run_migrate(silecsDocument, xmlSchema, versionOld, versionNew):
migration = MigrationBase(silecsDocument, xmlSchema, versionOld, versionNew)
migration.migrate()
def main_parse():
arguments = ParseMigrationArgs.parse_arguments()
......@@ -135,10 +139,8 @@ def main_parse():
arguments.xmlSchema,
arguments.versionOld,
arguments.versionNew)
def run_migrate(silecsDocument, xmlSchema, versionOld, versionNew):
migration = MigrationBase(silecsDocument, xmlSchema, versionOld, versionNew)
migration.migrate()
run_migrate(silecsDocument, xmlSchema, versionOld, versionNew)
if __name__ == "__main__":
main_parse()
......@@ -16,10 +16,14 @@ fi
mkdir -p ${RELEASE_DIR}
DEST_FILE=${RELEASE_DIR}/silecs.py
ENV_FILE=${RELEASE_DIR}/silecs_env.py
CONST_FILE=${RELEASE_DIR}/silecs_const.py
UTILS_FILE=${RELEASE_DIR}/silecs_utils.py
DEST_FILE_SCRIPT=${RELEASE_DIR}/silecs
cp -r ${SCRIPTPATH}/silecs.py ${DEST_FILE}
cp -r ${SCRIPTPATH}/silecs_env.py ${ENV_FILE}
cp -r ${SCRIPTPATH}/silecs_const.py ${CONST_FILE}
cp -r ${SCRIPTPATH}/silecs_utils.py ${UTILS_FILE}
cp -r ${SCRIPTPATH}/scripts/silecs ${DEST_FILE_SCRIPT}
......
This diff is collapsed.
import os
import getpass
USER = getpass.getuser()
SILECSDESIGN = "silecsdesign"
SILECSDEPLOY = "silecsdeploy"
DESIGN = "design"
DEPLOY = "deploy"
# FESA specific variables
try:
FESA_VERSION=(os.environ['FESA_VERSION'])
except KeyError:
FESA_VERSION="7.4.0"
FESA_BASE=f"/opt/fesa/fesa-fwk/{FESA_VERSION}"
FESA_XSD=f"{FESA_BASE}/fesa-model-gsi/xml/deployment/deployment-gsi.xsd"
FESA_GSI_TEMPLATE=f"{FESA_BASE}/fesa-model-gsi/xml/design/templates/GSIClassTemplate.xml"
# Silecs modules needed to import to generate code
SILECS_MODULES = ["genparam", "genplcsrc", "genduwrapper", "fillFESADeployUnit", "generateFesaDesign", "generateSourceCode"]
# Release param directory for releasing silecs parameter file
try:
RELEASE_PARAM_DIR=(os.environ['RELEASE_PARAM_DIR'])
except KeyError:
RELEASE_PARAM_DIR="/common/export/fesa/local"
\ No newline at end of file
import os
import sys
from packaging import version
import silecs_const
import silecs_utils
class SilecsEnvironment:
"""Class containing paths to silecs modules which are used in silecs cli tool"""
DESIGN_SHEMA_FILE="DesignSchema.xsd"
DEPLOY_SHEMA_FILE="DeploySchema.xsd"
def __init__(self, silecs_dir, silecs_version):
# silecs specific variables
self._SILECS_DIR = silecs_dir
self.update_silecs_variables(silecs_version)
def update_silecs_variables(self, silecs_version):
self._SILECS_VERSION = silecs_version
self.SILECS_BASE= os.path.join(self._SILECS_DIR, self._SILECS_VERSION)
self.DESIGN_SHEMA_PATH = f"{self.SILECS_BASE}/silecs-model/xml/{SilecsEnvironment.DESIGN_SHEMA_FILE}"
self.DEPLOY_SHEMA_PATH = f"{self.SILECS_BASE}/silecs-model/xml/{SilecsEnvironment.DEPLOY_SHEMA_FILE}"
self.SILECS_CODEGEN_BASE=f"{self.SILECS_BASE}/silecs-codegen/xml/"
self.SILECS_CODEGEN_MIGRATION=f"{self.SILECS_CODEGEN_BASE}/migration"
try:
self.SILECS_CODEGEN_FESA = silecs_utils.get_silecs_codegen_fesa(f"{self.SILECS_CODEGEN_BASE}/fesa", silecs_const.FESA_VERSION)
except Exception as e:
print(e)
sys.exit()
def get_available_versions(self):
"""Find available versions of silecs in SILECS_DIR"""
subfolders = [f.name for f in os.scandir(self._SILECS_DIR) if os.path.isdir(f)]
available_versions = []
for f in subfolders:
try:
version.Version(f)
available_versions.append(f)
except version.InvalidVersion:
pass
return sorted(available_versions, key= lambda x: version.Version(x))#
def get_version_underscored_tiny_as_x(self, 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_matching_versions(self, available_versions, old_version, new_version):
"""Get versions not older than old_version and not newer than new_version"""
is_between_old_new_version = lambda v: version.parse(v) >= version.parse(old_version) and version.parse(v) <= version.parse(new_version)
return [v for v in available_versions if is_between_old_new_version(v)]
def get_migration_modules(self, matching_versions, old_version):
"""Get migration modules that need to be run and from which to wchich
version """
migration_modules = []
matching_versions_underscored = [self.get_version_underscored_tiny_as_x(v) for v in matching_versions]
prev_version = old_version
migration_modules = []
for version in matching_versions:
if prev_version == version:
continue
prev_version_underscored = self.get_version_underscored_tiny_as_x(prev_version)
version_underscored = self.get_version_underscored_tiny_as_x(version)
migration_module = f"{prev_version_underscored}to{version_underscored}"
if not prev_version_underscored == version_underscored:
migration_modules += [(migration_module, prev_version, version)]
prev_version = version
return migration_modules
import os
import shutil
import datetime
from enum import Enum
from packaging import version
from xml.dom import minidom
import silecs_const
import silecs_env
class ProjectType(Enum):
"""Class used to describe type of project"""
DESIGN = 1
DEPLOY = 2
OTHER = 3
def get_version_underscored(version):
"""Get underscored version string eg. 2.3.4 -> 2_3_4"""
......@@ -30,4 +42,151 @@ def get_silecs_codegen_fesa(silecs_codegen_base_fesa, fesa_version):
print(f"Using codegen version {fesa_version}")
version_underscored = get_version_underscored(fesa_version)
return f"{silecs_codegen_base_fesa}/fesa_{version_underscored}/"
\ No newline at end of file
return f"{silecs_codegen_base_fesa}/fesa_{version_underscored}/"
def get_extension(filepath):
return filepath.split('.')[-1]
def is_silecs_design_file(filepath):
return get_extension(filepath) == silecs_const.SILECSDESIGN
def is_silecs_deploy_file(filepath):
return get_extension(filepath) == silecs_const.SILECSDEPLOY
def get_project_name(filepath):
"""Get project name
eg. workspace/TestClass/src/TestClass.silecsdesign -> TestClass
"""
return os.path.splitext(os.path.basename(filepath))[0]
def get_project_dir(filepath):
"""Get project dir
eg. workspace/TestClass/src/TestClass.silecsdesign -> workspace/TestClass/src"""
return os.path.dirname(filepath)
def get_project_path(filepath):
"""Get project path
eg. workspace/TestClass/src/TestClass.silecsdesign -> workspace/TestClass"""
return os.path.dirname(os.path.dirname(filepath))
def get_filename(filepath):
"""Get filename from path
eg. workspace/TestClass/src/TestClass.silecsdesign -> TestClass.silecsdesign
"""
_, filename = os.path.split(filepath)
return filename
def get_project_type(project_path):
"""Get project type based on presence of silecs design or deploy file in project_path"""
project_path_src = os.path.join(project_path, "src")
silecs_design_files = [f for f in os.listdir(project_path_src) if os.path.isfile(os.path.join(project_path_src, f)) and is_silecs_design_file(f)]
silecs_deploy_files = [f for f in os.listdir(project_path_src) if os.path.isfile(os.path.join(project_path_src, f)) and is_silecs_deploy_file(f)]
project_type = ProjectType.OTHER
if silecs_design_files:
project_type = ProjectType.DESIGN
elif silecs_deploy_files:
project_type = ProjectType.DEPLOY
return project_type
def get_schema_path(silecs_env, silecs_design_deploy_path):
"""Get schema for silecs design/deploy XML validation"""
extension = get_extension(silecs_design_deploy_path)
if extension == silecs_const.SILECSDESIGN:
return silecs_env.DESIGN_SHEMA_PATH
if extension == silecs_const.SILECSDEPLOY:
return silecs_env.DEPLOY_SHEMA_PATH
raise Exception(f"Error: Passed file for validation needs to have the extension '.{silecs_const.SILECSDESIGN}' or '.{silecs_const.SILECSDEPLOY}'")
def get_silecs_file_path_from_fesa(fesa_design_deploy_path):
"""Get schema for silecs design/deploy XML validation"""
extension = get_extension(fesa_design_deploy_path)
project_name = get_project_name(fesa_design_deploy_path)
project_dir = get_project_dir(fesa_design_deploy_path)
if extension == silecs_const.DESIGN:
extension = silecs_const.SILECSDESIGN
elif extension == silecs_const.DEPLOY:
extension = silecs_const.SILECSDEPLOY
else:
raise Exception(f"Error: Passed FESA file needs to have the extension '.{silecs_const.DESIGN}' or '.{silecs_const.DEPLOY}'")
return os.path.join(project_dir, project_name + "." + extension)
def create_backup_file(filepath):
backup_file = filepath + ".backup"
with open(backup_file, 'w+'):
shutil.copyfile(src=filepath, dst=backup_file)
def get_silecs_design_template(project_name, silecs_version, design_schema_path):
date = datetime.datetime.today().strftime('%m/%d/%Y')
return f"""<?xml version="1.0" encoding="UTF-8"?>
<SILECS-Design silecs-version="{silecs_version}" created="{date}" updated="{date}"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="{design_schema_path}">
<Information>
<Owner user-login="{silecs_const.USER}"/>
<Editor user-login="{silecs_const.USER}"/>
</Information>
<SILECS-Class name="{project_name}" version="0.1.0" domain="OPERATIONAL" >
<Acquisition-Block name="MyBlock" generateFesaProperty="true">
<Acquisition-Register name="myRegister" generateFesaValueItem="true">
<scalar format="int32"/>
</Acquisition-Register>
</Acquisition-Block>
</SILECS-Class>
</SILECS-Design>"""
def get_silecs_deploy_template(project_name, silecs_version, deploy_schema_path):
date = datetime.datetime.today().strftime('%m/%d/%Y')
return f"""<?xml version="1.0" encoding="UTF-8"?>
<SILECS-Deploy silecs-version="{silecs_version}" created="{date}" updated="{date}"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="{deploy_schema_path}">
<Information>
<Owner user-login="{silecs_const.USER}"/>
<Editor user-login="{silecs_const.USER}"/>
</Information>
<Deploy-Unit name="{project_name}" version="0.1.0"/>
<SilecsDesign silecs-design-name="" silecs-design-version=""/>
<Controller host-name="">
<Siemens-PLC system="TIA-PORTAL" model="SIMATIC_S7-300" protocol="DEVICE_MODE" base-DB-number="1">
<Device silecs-device-label="dev0" silecs-design-ref="" fesa-device-name="" fesa-fec-name=""/>
</Siemens-PLC>
</Controller>
</SILECS-Deploy>"""
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-Deploy")[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 get_silecs_desing_deploy_file(project_path):
project_path_src = os.path.join(project_path, "src")
is_silecs_deploy_design = lambda f: f.split(".")[-1] == silecs_const.SILECSDESIGN or f.split(".")[-1] == silecs_const.SILECSDEPLOY
silecs_design_deploy_files = [f for f in os.listdir(project_path_src) if os.path.isfile(os.path.join(project_path_src, f)) and is_silecs_deploy_design(f)]
if not silecs_design_deploy_files:
return Exception("No silecs design/deploy files found for that project")
elif len(silecs_design_deploy_files) > 1 :
return Exception("In directory should be one silecs design/deploy file")
return os.path.abspath(os.path.join(project_path_src, silecs_design_deploy_files[0]))
def get_silecs_version_from_project(project_path):
try:
silecs_desing_deploy_file = get_silecs_desing_deploy_file(project_path)
return get_silecs_version_from_file(silecs_desing_deploy_file)
except Exception as e:
print(e)
\ No newline at end of file
......@@ -10,8 +10,8 @@ RELEASE_PARAM_DIR_PATH = os.path.join("tests", "releaseParamDir")
os.environ["RELEASE_PARAM_DIR"] = RELEASE_PARAM_DIR_PATH
sys.path.append('../')
import silecs
from silecs_cli import silecs, silecs_utils
DESIGN_PATH = os.path.join("tests", "testClasses", "SilecsTestClass", "src", "SilecsTestClass.design")
DEPLOY_PATH = os.path.join("tests", "testClasses", "SilecsTestClass_DU", "src", "SilecsTestClass_DU.deploy")
......@@ -26,8 +26,7 @@ RELEASE_PARAM_FILEPATH_DEV = os.path.join(RELEASE_PARAM_DIR_PATH, "SilecsTestCla
design_schema_path_mock = "../silecs-model/src/xml/DesignSchema.xsd"
deploy_schema_path_mock = "../silecs-model/src/xml/DeploySchema.xsd"
@patch('silecs.DESIGN_SHEMA_PATH', design_schema_path_mock)
@patch('silecs.DEPLOY_SHEMA_PATH', deploy_schema_path_mock)
class SilecsTest(unittest.TestCase):
"""Class to test silecs.py script"""
@classmethod
......@@ -50,14 +49,14 @@ class SilecsTest(unittest.TestCase):
filenames_extensions = [("test1.design", "design"), ("test2.deploy", "deploy")]
for filename, expected in filenames_extensions:
with self.subTest():
self.assertEqual(silecs.get_extension(filename), expected)
self.assertEqual(silecs_utils.get_extension(filename), expected)
def test_get_project_name(self):
"""Test getting extension from filepath"""
filenames_project_names = [("test1.design", "test1"), ("test2.deploy", "test2")]
for filename, project_name in filenames_project_names:
with self.subTest():
self.assertEqual(silecs.get_project_name(filename), project_name)
self.assertEqual(silecs_utils.get_project_name(filename), project_name)
def test_get_project_path(self):
"""Test getting project path from filepath"""
......@@ -65,7 +64,7 @@ class SilecsTest(unittest.TestCase):
("workspace/test2/src/test2.deploy", "workspace/test2")]
for filename, project_path in filenames_project_paths:
with self.subTest():
self.assertEqual(silecs.get_project_path(filename), project_path)
self.assertEqual(silecs_utils.get_project_path(filename), project_path)
def test_get_project_dir(self):
"""Test getting project directory from filepath"""
......@@ -73,101 +72,91 @@ class SilecsTest(unittest.TestCase):
("workspace/test2/src/test2.deploy", "workspace/test2/src")]
for filename, directory in filenames_dirs:
with self.subTest():
self.assertEqual(silecs.get_project_dir(filename), directory)
self.assertEqual(silecs_utils.get_project_dir(filename), directory)
def test_get_schema_path(self):
"""Test getting xml schema path for silecs design deploy files"""
filenames_schema_paths = [("/home/test/example/test1.silecsdesign",
"../silecs-model/src/xml/DesignSchema.xsd"),
"/common/usr/cscofe/silecs/2.3.0/silecs-model/xml/DesignSchema.xsd"),
("/home/test/example/test2.silecsdeploy",
"../silecs-model/src/xml/DeploySchema.xsd")]
"/common/usr/cscofe/silecs/2.3.0/silecs-model/xml/DeploySchema.xsd")]
silecs_cli = silecs.SilecsCli()
for filename, schema_path in filenames_schema_paths:
with self.subTest():
self.assertEqual(silecs.get_schema_path(filename), schema_path)
self.assertEqual(silecs_utils.get_schema_path(silecs_cli.silecs_env, filename), schema_path)
def test_get_schema_path_invalid(self):
"""Test getting xml schema path for invalid path (passed file path
must have the extension '.silecsdesign' or '.silecsdeploy')"""
try:
silecs.get_schema_path("/home/test/example/test1.design")
silecs_cli = silecs.SilecsCli()
silecs_utils.get_schema_path(silecs_cli.silecs_env, "/home/test/example/test1.design")
except Exception as e:
self.assertEqual("Error: Passed file for validation needs to have the extension '.silecsdesign' or '.silecsdeploy'", str(e))
def test_get_silecs_file_path_from_fesa(self):
"""Test getting xml schema path for silecs design deploy files"""
self.assertEqual(silecs.get_silecs_file_path_from_fesa("../test1.design"), os.path.join("..", "test1.silecsdesign"))
self.assertEqual(silecs_utils.get_silecs_file_path_from_fesa("../test1.design"), os.path.join("..", "test1.silecsdesign"))
def test_get_silecs_file_path_from_fesa_invalid(self):
"""Test getting xml schema path for invalid file (passed file path
must have the extension '.design' or '.deploy')
"""
try:
silecs.get_silecs_file_path_from_fesa("../test1.silecsdesign")
silecs_utils.get_silecs_file_path_from_fesa("../test1.silecsdesign")
except Exception as e:
self.assertEqual("Error: Passed FESA file needs to have the extension '.design' or '.deploy'", str(e))
def test_create_backup_file(self):
"""Test creating backup file"""
silecs.create_backup_file(DESIGN_PATH)
silecs_utils.create_backup_file(DESIGN_PATH)
self.assertNotEqual(os.stat(BACKUP).st_size, 0)
def test_silecs_validate(self):
"""Test validating silecs design/deploy files"""
silecs_cli = silecs.SilecsCli()
silecs_design_deploy_paths_result = [
(os.path.join("tests", "testClasses", "SilecsTestClass_DU", "src", "SilecsTestClass_DU.silecsdeploy"), True), \
(os.path.join("tests", "examples", "SchneiderM340TestDU_invalid.silecsdeploy"), False), \
(os.path.join("tests", "examples", "SilecsTestClassDU_invalid.silecsdeploy"), False)
]
for path, expected_result in silecs_design_deploy_paths_result:
xsd_path = silecs.get_schema_path(path)
self.assertEqual(silecs.validate(path, xsd_path), expected_result)
def test_create_validate(self):
"""Test creating silecs design file and then validate it"""
try:
new_file_path = silecs.get_silecs_file_path_from_fesa(DESIGN_PATH)
silecs.create(new_file_path)
except Exception as e:
print(e)
xsd_path = silecs.get_schema_path(new_file_path)
self.assertEqual(silecs.validate(new_file_path, xsd_path), True)
if os.path.isfile(new_file_path):
os.remove(new_file_path)
xsd_path = silecs_utils.get_schema_path(silecs_cli.silecs_env, path)
self.assertEqual(silecs_cli.validate(path, xsd_path), expected_result)
def test_create_silecs_design(self):
"""Test creating silecs design files from design file"""
path = os.path.abspath(DESIGN_PATH)
new_file_path = silecs.get_silecs_file_path_from_fesa(path)
silecs.create(new_file_path)
silecs_cli = silecs.SilecsCli()
new_file_path = silecs_utils.get_silecs_file_path_from_fesa(path)
silecs_cli.create(new_file_path)
print(new_file_path)
self.assertEqual(os.path.exists(new_file_path), True)
self.assertNotEqual(os.stat(new_file_path).st_size, 0)
try:
silecs.create(new_file_path)
silecs_cli.create(new_file_path)
except Exception as e:
self.assertIn("There is already a .silecsdesign file available:", str(e))
def test_create_silecs_deploy(self):
"""Test creating silecs deploy files from deploy file"""
path = os.path.abspath(DEPLOY_PATH)
silecs_cli = silecs.SilecsCli()
new_file_path = silecs.get_silecs_file_path_from_fesa(path)
new_file_path = silecs_utils.get_silecs_file_path_from_fesa(path)
try:
silecs.create(new_file_path)
silecs_cli.create(new_file_path)
except Exception as e:
print(str(e))
self.assertIn("There is already a .silecsdeploy file available:", str(e))
def test_release_param(self):
"""Test release param file"""
result = silecs.release(SILECS_DEPLOY_PATH)
silecs_cli = silecs.SilecsCli()
result = silecs_cli.release(SILECS_DEPLOY_PATH)
self.assertEqual(result, True)
self.assertEqual(os.path.isfile(RELEASE_PARAM_FILEPATH), True)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment