Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • silecs/opensilecs
  • k.fugiel/opensilecs
  • s.kupiecki/opensilecs
3 results
Show changes
Showing
with 594 additions and 490 deletions
......@@ -43,13 +43,13 @@ def genClassHeader(workspacePath, deploy, design, funcGetSilecsDesignFilePath, f
classDeclarations += genduwrappertemplate.getBlockClass(block,registerInitializerList,registerGetterSetter,registersDimentionsDeclaration,registersDeclaration)
blockGetters = ""
sendRecvBlocks = genduwrappertemplate.getDeviceSendRecvBlocks(designClass.getBlockNodes())
sendRecvBlocks = genduwrappertemplate.getDeviceSendRecvBlocks(designClass.getBlockNodes(), designClass.customTypesXmlNode)
for block in designClass.getDesignBlocks():
blockGetters += genduwrappertemplate.getDeviceBlockGetterSetter(block)
classDeclarations += genduwrappertemplate.getDeviceClass(blockGetters,sendRecvBlocks)
sendRecvBlocks = genduwrappertemplate.getControllerSendRecvBlocks(designClass.getBlockNodes())
sendRecvBlocks = genduwrappertemplate.getControllerSendRecvBlocks(designClass.getBlockNodes(), designClass.customTypesXmlNode)
classDeclarations = genduwrappertemplate.getDesignClass(design.name, design.version)
designWrapper = genduwrappertemplate.designFileTemplate.substitute({'designNameCapitalized' : iecommon.capitalizeString(design.name),'designNameUpper' : design.name.upper(),'classDeclarations' : classDeclarations})
......
......@@ -337,10 +337,10 @@ controllerSendTemplate = string.Template("""\
""")
def getControllerSendRecvBlocks(blockList):
def getControllerSendRecvBlocks(blockList, customTypesXmlNode):
text = ""
for blockNode in blockList:
block = DesignBlock(blockNode)
block = DesignBlock(blockNode, customTypesXmlNode)
map = {'blockName' : block.name,
'blockNameCapitalized' : block.getNameCapitalized()}
if block.isReadable():
......@@ -405,39 +405,39 @@ ${blockInitialization}\
matrixRegisterAssignementGetter = string.Template("""\
// Copy register ${regName}
${cType} *__${regName} = new ${cType}[block.${regName}Dim1 * block.${regName}Dim2];
getSilecsDevice()->getRegister("${regName}")->getVal${silecsTypeCapitalized}Array2D(__${regName}, block.${regName}Dim1, block.${regName}Dim2);
getSilecsDevice()->getRegister("${regName}")->getValArray2D<${cType}>(__${regName}, block.${regName}Dim1, block.${regName}Dim2);
block.set${regNameCapitalized}(__${regName});
delete[] __${regName};
""")
arrayRegisterAssignementGetter = string.Template("""\
// Copy register ${regName}
${cType} *__${regName} = new ${cType}[block.${regName}Dim1];
getSilecsDevice()->getRegister("${regName}")->getVal${silecsTypeCapitalized}Array(__${regName}, block.${regName}Dim1);
getSilecsDevice()->getRegister("${regName}")->getValArray<${cType}>(__${regName}, block.${regName}Dim1);
block.set${regNameCapitalized}(__${regName});
delete[] __${regName};
""")
scalarRegisterAssignementGetter = string.Template("""\
// Copy register ${regName}
block.set${regNameCapitalized}(getSilecsDevice()->getRegister("${regName}")->getVal${silecsTypeCapitalized}());
block.set${regNameCapitalized}(getSilecsDevice()->getRegister("${regName}")->getVal<${cType}>());
""")
matrixRegisterAssignementSetter = string.Template("""\
// Copy register ${regName}
${cType} *__${regName} = new ${cType}[block.${regName}Dim1 * block.${regName}Dim2];
block.get${regNameCapitalized}(__${regName});
getSilecsDevice()->getRegister("${regName}")->setVal${silecsTypeCapitalized}Array2D(__${regName}, block.${regName}Dim1, block.${regName}Dim2);
getSilecsDevice()->getRegister("${regName}")->setValArray2D<${cType}>(__${regName}, block.${regName}Dim1, block.${regName}Dim2);
delete[] __${regName};
""")
arrayRegisterAssignementSetter = string.Template("""\
// Copy register ${regName}
${cType} *__${regName} = new ${cType}[block.${regName}Dim1];
block.get${regNameCapitalized}(__${regName});
getSilecsDevice()->getRegister("${regName}")->setVal${silecsTypeCapitalized}Array(__${regName}, block.${regName}Dim1);
getSilecsDevice()->getRegister("${regName}")->setValArray<${cType}>(__${regName}, block.${regName}Dim1);
delete[] __${regName};
""")
scalarRegisterAssignementSetter = string.Template("""\
// Copy register ${regName}
getSilecsDevice()->getRegister("${regName}")->setVal${silecsTypeCapitalized}(block.get${regNameCapitalized}());
getSilecsDevice()->getRegister("${regName}")->setVal<${cType}>(block.get${regNameCapitalized}());
""")
def getDeviceBlockGetterSetter(block):
......@@ -447,7 +447,6 @@ def getDeviceBlockGetterSetter(block):
for register in block.getDesignRegisters():
map = {'regName' : register.name,
'regNameCapitalized' : register.getNameCapitalized(),
'silecsTypeCapitalized' : register.getSilecsTypeCapitalized(),
'cType' : register.getCType()}
if register.isArray2D():
blockInitialization += matrixRegisterAssignementGetter.substitute(map)
......@@ -465,7 +464,6 @@ def getDeviceBlockGetterSetter(block):
for register in block.getDesignRegisters():
map = {'regName' : register.name,
'regNameCapitalized' : register.getNameCapitalized(),
'silecsTypeCapitalized' : register.getSilecsTypeCapitalized(),
'cType' : register.getCType()}
if register.isArray2D():
blockInitialization += matrixRegisterAssignementSetter.substitute(map)
......@@ -502,10 +500,10 @@ deviceSendTemplate = string.Template("""\
""")
def getDeviceSendRecvBlocks(blockList):
def getDeviceSendRecvBlocks(blockList, customTypesXmlNode):
text = ""
for blockNode in blockList:
block = DesignBlock(blockNode)
block = DesignBlock(blockNode, customTypesXmlNode)
map = {'blockName' : block.name,
'blockNameCapitalized' : block.getNameCapitalized()}
if block.isReadable():
......
......@@ -36,6 +36,9 @@ from model.Deploy.Controller import *
import model.Deploy.Device
from model.Param.Param import *
# maximum number of boolean bits in the same register address
MAX_BITS_IN_ADDRESS = 8
#-------------------------------------------------------------------------
# Trimming: remove new-line, tabulation and spaces of the string
# Used to compute CRC32 on significant data only
......@@ -49,30 +52,30 @@ def trim (str):
def computeChecksumController( workspacePath, deploy, controller, silecsVersion, funcGetSilecsDesignFilePath, logTopics={'errorlog': True}):
majorSilecsVersion = iecommon.getMajorSilecsVersion(silecsVersion)
CRC32 = zlib.crc32(trim(str(majorSilecsVersion)),0)& 0xffffffff
CRC32 = zlib.crc32(trim(str(controller.address[MEM_TYPE])),CRC32)& 0xffffffff
CRC32 = zlib.crc32(trim(str(majorSilecsVersion)).encode(),0)& 0xffffffff
CRC32 = zlib.crc32(trim(str(controller.address[MEM_TYPE])).encode(),CRC32)& 0xffffffff
for silecsDesign in deploy.silecsDesigns:
devices = controller.getDevicesOfDesign(silecsDesign.name)
if len(devices) > 0:
CRC32 = zlib.crc32(trim(silecsDesign.name),CRC32)& 0xffffffff
CRC32 = zlib.crc32(trim(silecsDesign.version),CRC32)& 0xffffffff
CRC32 = zlib.crc32(trim(silecsDesign.name).encode(),CRC32)& 0xffffffff
CRC32 = zlib.crc32(trim(silecsDesign.version).encode(),CRC32)& 0xffffffff
designDOM = iefiles.loadSilecsDesignDOM(workspacePath, silecsDesign, silecsVersion, funcGetSilecsDesignFilePath)
CRC32 = computeChecksumClass(designDOM,CRC32,logTopics)
for device in devices:
CRC32 = zlib.crc32(trim(device.silecsDeviceLabel),CRC32)& 0xffffffff
CRC32 = zlib.crc32(trim(device.silecsDeviceLabel).encode(),CRC32)& 0xffffffff
iecommon.logInfo("CRC32: %s" % str(CRC32),logTopics)
return CRC32
def computeChecksumClass(designDOM, CRC32, logTopics={'errorlog': True}):
designClass = DesignClass.getDesignClassFromRootNode(designDOM)
for block in designClass.getDesignBlocks():
CRC32 = zlib.crc32(trim(block.name),CRC32)& 0xffffffff
for block in designClass.getDesignMemoryBlocks():
CRC32 = zlib.crc32(trim(block.name).encode(),CRC32)& 0xffffffff
for register in block.getDesignRegisters():
CRC32 = zlib.crc32(trim(register.name),CRC32)& 0xffffffff
CRC32 = zlib.crc32(trim(register.format),CRC32)& 0xffffffff
CRC32 = zlib.crc32(trim(str(register.dim1)),CRC32)& 0xffffffff
CRC32 = zlib.crc32(trim(str(register.dim2)),CRC32)& 0xffffffff
CRC32 = zlib.crc32(trim(str(register.stringLength)),CRC32)& 0xffffffff
CRC32 = zlib.crc32(trim(register.name).encode(),CRC32)& 0xffffffff
CRC32 = zlib.crc32(trim(register.format).encode(),CRC32)& 0xffffffff
CRC32 = zlib.crc32(trim(str(register.dim1)).encode(),CRC32)& 0xffffffff
CRC32 = zlib.crc32(trim(str(register.dim2)).encode(),CRC32)& 0xffffffff
CRC32 = zlib.crc32(trim(str(register.stringLength)).encode(),CRC32)& 0xffffffff
return CRC32
# Needed to encapsulate "genParam" in order to allow unit-testing (fake all file interactions)
......@@ -80,7 +83,7 @@ def genParamBase( funcGetSilecsDesignFilePath, funcGetParameterFile, funcGetSile
# Check the Deployment document exist for that PLC
deployPath = funcGetSilecsDeployFilePath(workspacePath, deployName)
if(not os.path.isfile(deployPath)):
iecommon.logError(deployName + "deployment file cannot be found in provided workspace",True,logTopics)
iecommon.logError(deployPath + ": '" + deployName + "' cannot be found in provided workspace '" + workspacePath + "'." ,True,logTopics)
deploy = Deploy.getDeployFromFile(deployPath)
......@@ -94,7 +97,7 @@ def genParamBase( funcGetSilecsDesignFilePath, funcGetParameterFile, funcGetSile
for controller in deploy.controllers:
mappings = { MEM_TYPE: None, DI_TYPE: None, DO_TYPE: None, AI_TYPE: None, AO_TYPE: None }
for mappingType, mapping in mappings.iteritems():
for mappingType, mapping in mappings.items():
mappings[mappingType] = Mapping(controller.address[mappingType], mappingType)
# Messagges for debugging purpose
......@@ -104,7 +107,7 @@ def genParamBase( funcGetSilecsDesignFilePath, funcGetParameterFile, funcGetSile
iecommon.logDebug("plcModel = " + controller.model,logTopics)
iecommon.logDebug("plcBrand = " + controller.brand,logTopics)
iecommon.logDebug("plcProtocol = " + controller.protocol,logTopics)
for mappingType, mapping in mappings.iteritems():
for mappingType, mapping in mappings.items():
iecommon.logDebug("address" + mappingType2String[mappingType] + " = " + str(controller.address[mappingType]),logTopics)
paramFile = funcGetParameterFile(workspacePath, deployName, controller.hostName )
......@@ -123,14 +126,21 @@ def genParamBase( funcGetSilecsDesignFilePath, funcGetParameterFile, funcGetSile
iecommon.logDebug("-----------------------------------------",logTopics)
iecommon.logDebug("------ Analysing Class " + deployDesign.name + " ------",logTopics)
iecommon.logDebug("-----------------------------------------",logTopics)
designDOM = iefiles.loadSilecsDesignDOM(workspacePath, deployDesign, silecsVersion, funcGetSilecsDesignFilePath)
if iecommon.supportsDateTimeLong(controller):
datetime_format = xmltemplate.DATE_TIME_LONG
else:
datetime_format = xmltemplate.DATE_AND_TIME
iecommon.logDebug("Datetime format is:", datetime_format)
designDOM = iefiles.loadSilecsDesignDOM(workspacePath, deployDesign, silecsVersion, funcGetSilecsDesignFilePath, datetime_format)
designClass = DesignClass.getDesignClassFromRootNode(designDOM)
paramClass = ParamClass()
paramClass.initWithDesignClass(designClass)
param.controller.addParamClass(paramClass)
controller.blockCounter = 0
for mappingType, mapping in mappings.iteritems():
for mappingType, mapping in mappings.items():
mapping.blockAddress = 0
mapping.nbBlock = 0
mapping.deviceDataSize = 0
......@@ -138,6 +148,10 @@ def genParamBase( funcGetSilecsDesignFilePath, funcGetParameterFile, funcGetSile
# INNER LOOP TO ACCESS AT BLOCK LEVEL
for designBlock in designClass.getDesignBlocks():
# Siemens does not support to read back IO-Setting registers TODO: What about other PLC types ?
if controller.brand == "SIEMENS" and designBlock.isIOBlock() and designBlock.isSetting():
raise Exception("Error: Siemens PLC's do not support to read back IO-Settings. Please use 'Command-IO-Block' instead of 'Setting-IO-Block' in the silecsdesign")
paramBlock = ParamBlock()
paramBlock.initWithDesignBlock(designBlock)
......@@ -147,9 +161,37 @@ def genParamBase( funcGetSilecsDesignFilePath, funcGetParameterFile, funcGetSile
blockSize = 0 # block size (sum of the register size)
blockMemSize = 0 # block size (sum of the regist)
controller.regCounter = 0 # used for NI register address generation
# additional data for managing booleans
isBooleanRegister = False
bitNumber = 0
nextRegister = None
# INNER LOOP TO ACCESS AT REGISTER LEVEL
for designRegister in designBlock.getDesignRegisters():
designRegisters = designBlock.getDesignRegisters()
for i, designRegister in enumerate(designRegisters):
if i + 1 != len(designRegisters):
nextRegister = designRegisters[i+1]
else:
nextRegister = None
iecommon.logDebug("------ Processing Register " + designRegister.name + " ------",logTopics)
# Allow bool register only for Siemens controller
if designRegister.format == 'bool':
isBooleanRegister = True
if not designRegister.isScalar():
iecommon.logError("Invalid format in register: %s Bool format available only for scalars" %designRegister.name, False, logTopics)
sys.exit(2)
if controller.brand != 'SIEMENS':
iecommon.logError("Invalid format in register: %s. Bool format available only on SIEMENS controllers" %designRegister.name, False,logTopics)
sys.exit(2)
else:
isBooleanRegister = False
bitNumber = 0
# Set length attribute only for string registers
if designRegister.format == 'string': # TODO: Port this constraint to java
if controller.brand == 'DIGI':
......@@ -160,12 +202,25 @@ def genParamBase( funcGetSilecsDesignFilePath, funcGetParameterFile, funcGetSile
sys.exit(2)
regAddress = controller.alignRegAddress(designBlock, designRegister, regAddress)
controller.regCounter += 1 # used for NI register address generation
# Set register mem-size
paramRegister = ParamRegister()
paramRegister.initWithDesignRegister(designRegister,regAddress,controller.msize)
# Compute address for the next register
regAddress = controller.computeNextRegAddress(designBlock, designRegister, regAddress)
if isBooleanRegister:
paramRegister.initWithDesignRegister(designRegister,regAddress,controller.msize, bitNumber)
else:
paramRegister.initWithDesignRegister(designRegister,regAddress,controller.msize)
if isBooleanRegister and nextRegister and nextRegister.format == "bool":
bitNumber = (bitNumber + 1) % MAX_BITS_IN_ADDRESS
if bitNumber == 0:
# Compute address for the next register if max bits in address achieved
regAddress = controller.computeNextRegAddress(designBlock, designRegister, regAddress)
else:
# Compute address for the next register
regAddress = controller.computeNextRegAddress(designBlock, designRegister, regAddress)
paramBlock.xmlNode.addChild(paramRegister.xmlNode)
#paramRegister.xmlNode.shellPrintNode()
#iterativelly compute the block size (accumulator initialized outside the loop)
......@@ -180,24 +235,25 @@ def genParamBase( funcGetSilecsDesignFilePath, funcGetParameterFile, funcGetSile
paramClass.xmlNode.addChild(paramBlock.xmlNode)
# END OF INNER LOOP TO ACCESS AT BLOCK LEVEL (LOOP-2)
# Set block Address
for mappingType, mapping in mappings.iteritems():
for mappingType, mapping in mappings.items():
paramClass.setAddress(mapping.classBaseAddress,mappingType)
for device in devices:
instance = libxml2.newNode("Instance")
instance.setProp("label", device.silecsDeviceLabel)
instance.setProp("address", str(controller.computeInstAddress(mappings[MEM_TYPE])))
instance.setProp("DI-address", str(controller.computeInstAddress(mappings[DI_TYPE])))
instance.setProp("DO-address", str(controller.computeInstAddress(mappings[DO_TYPE])))
instance.setProp("AI-address", str(controller.computeInstAddress(mappings[AI_TYPE])))
instance.setProp("AO-address", str(controller.computeInstAddress(mappings[AO_TYPE])))
instance.setProp("fesa-label", device.fesaDeviceName)
instance.setProp("address", str(int(controller.computeInstAddress(mappings[MEM_TYPE]))))
instance.setProp("DI-address", str(int(controller.computeInstAddress(mappings[DI_TYPE]))))
instance.setProp("DO-address", str(int(controller.computeInstAddress(mappings[DO_TYPE]))))
instance.setProp("AI-address", str(int(controller.computeInstAddress(mappings[AI_TYPE]))))
instance.setProp("AO-address", str(int(controller.computeInstAddress(mappings[AO_TYPE]))))
paramClass.xmlNode.addChild(instance)
# Compute the memory address for the next class
for mappingType, mapping in mappings.iteritems():
for mappingType, mapping in mappings.items():
controller.computeNextBaseAddress(mapping, nbDevice);
for mappingType, mapping in mappings.iteritems():
for mappingType, mapping in mappings.items():
paramClass.setUsedData(mapping.usedData,mappingType)
iecommon.logInfo("Used-"+ mappingType2String[mappingType] +" for Class " + deployDesign.name + ": " + mapping.usedData ,logTopics)
......@@ -207,7 +263,7 @@ def genParamBase( funcGetSilecsDesignFilePath, funcGetParameterFile, funcGetSile
plcUsedMem = "DB"+str(controller.address[MEM_TYPE])+"..DB"+str(controller.memLast)+" / "+str(controller.memSize)+" bytes"
else:
# SCHNEIDER works with absolute addressing
startAddr = controller.address[MEM_TYPE] /2 #Memory info uses word-addressing
startAddr = int(controller.address[MEM_TYPE] /2) #Memory info uses word-addressing
plcUsedMem = "MW"+str(startAddr)+"..MW"+str(controller.memLast)+" / "+str(controller.memSize)+" words"
# print plc used memory
......
......@@ -18,6 +18,8 @@ import sys
import time
import zlib
import libxml2
import datetime
import string
import iecommon
import iefiles
......@@ -62,7 +64,8 @@ whichUnityFormat = {
'int' : 'INT',
'dint' : 'DINT',
'real' : 'REAL',
'dt' : 'DT'
'dt' : 'DT',
'dtl' : 'DTL'
}
whichTwincatFormat = {
......@@ -74,9 +77,9 @@ whichTwincatFormat = {
'int32' : 'DINT',
'float32' : 'REAL',
'string' : 'STRING',
# 'uint64' not supported by PLCs
# 'int64' not supported by PLCs
# 'float64' not supported by PLCs
'uint64' : 'ULINT',
'int64' : 'LINT',
'float64' : 'LREAL',
'date' : 'DT',
'char' : 'SINT',
'byte' : 'BYTE',
......@@ -89,8 +92,8 @@ whichTwincatFormat = {
'string' : 'STRING'
}
schneiderRegArray = """ARRAY[0..%d] OF %s"""
schneiderRegArray2d = """ARRAY[0..%d, 0..%d] OF %s"""
schneiderRegArray = string.Template("""ARRAY[0..${value}] OF ${len}""")
schneiderRegArray2d = string.Template("""ARRAY[0..${value1}, 0..${value2}] OF ${len}""")
# Beckhoff templates (using newlines \r\n because TwinCAT compiler complains with line feed character)
beckhoffReg = """ %s_%04x_%s AT %%MW%s: %s;\r\n\r\n"""
......@@ -112,26 +115,35 @@ def xsyRegister(register):
if register.isString():
return strLen
elif register.isStringArray():
return schneiderRegArray %(register.dim1-1, strLen)
return schneiderRegArray.substitute(value=register.dim1-1, len=strLen)
else: #2D-Array
return schneiderRegArray2d %(register.dim1-1, register.dim2-1, strLen)
return schneiderRegArray2d.substitute(value1=register.dim1-1, value2=register.dim2-1, len=strLen)
else:
if register.isScalar():
return whichUnityFormat[register.format]
elif register.isArray():
return schneiderRegArray %(register.dim1-1, whichUnityFormat[register.format])
return schneiderRegArray.substitute(value=register.dim1-1, len=whichUnityFormat[register.format])
else: #2D-Array
return schneiderRegArray2d %(register.dim1-1, register.dim2-1, whichUnityFormat[register.format])
return schneiderRegArray2d.substitute(value1=register.dim1-1, value2=register.dim2-1, len=whichUnityFormat[register.format])
def getDateTime():
dt = time.localtime(time.time())
return "DT#%s-%s-%s-%s:%s:%s" %(dt[0],dt[1],dt[2],dt[3],dt[4],dt[5])
def decimalToBCD(value):
return ((value//10)*16)+(value%10)
# convert date to DATE_TIME_LONG or DATE_AND_TIME string format (Siemens SIMATIC PLC)
def getDateTime(controller):
dt = datetime.datetime.now()
if iecommon.supportsDateTimeLong(controller):
return "DTL#%s-%s-%s-%s:%s:%s.%03d" %(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond // 1000)
else:
return "DT#%s-%s-%s-%s:%s:%s" % (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)
#-------------------------------------------------------------------------
# SIEMENS PLC code generation
#-------------------------------------------------------------------------
def generateSiemensSources(param, sourceFolderPath ,logTopics):
stlString = ''
stlString = s7template.head.substitute()
symString = '' # for .sdf Symbol file
# Prepare the Simatic Symbols (<.sdf> file)
......@@ -142,20 +154,21 @@ def generateSiemensSources(param, sourceFolderPath ,logTopics):
for paramClass in param.controller.paramClasses:
deviceDOMList = paramClass.getDeviceInstanceNodes() # get Device instances of that class
# Generate the Blocks definition
for blockIndex, block in enumerate(paramClass.getParamBlocks()):
for blockIndex, block in enumerate(paramClass.getParamMemoryBlocks()):
registerList = ''
for register in block.getParamRegisters():
# If PLC does not supports this register format abort this generation and log the error
if(register.format in ['uint64','int64','float64']):
iecommon.logError('ERROR: In design %s_%s register %s, %s format not supported for current controller model.'
%(paramClass.name, paramClass.version, register.name, register.format)
, True,logTopics)
#FLO: DO NOT LOG ERROR IF TWINCAT3 !!
# If PLC does not supports this register format abort this generation and log the error
# if(registerFormat in ['uint64','int64','float64']):
#iecommon.logGenerationResult(iecommon.opts.username,'ERROR: In register '+registerName+', '+registerFormat+' format not supported for current controller model.')
#iecommon.logError('ERROR: In register '+registerName+', '+registerFormat+' format not supported for current controller model.', True)
#create register value if required (diagnostic registers assignment in particular)
registerValue = s7template.stlRegisterValue(register.name, param.owner, getDateTime(), param.checksum,param.silecsVersion)
registerValue = s7template.stlRegisterValue(register.name, param.owner, getDateTime(param.controller), param.checksum,param.silecsVersion)
if register.format == 'string':
registerList += s7template.stlRegister(register.name, register.format, register.dim1, register.dim2, registerValue, long(register.stringLength))
registerList += s7template.stlRegister(register.name, register.format, register.dim1, register.dim2, registerValue, int(register.stringLength))
else:
registerList += s7template.stlRegister(register.name, register.format, register.dim1, register.dim2, registerValue)
......@@ -167,22 +180,24 @@ def generateSiemensSources(param, sourceFolderPath ,logTopics):
# Generate the Data-Blocks: one DB per Device instance
for deviceIndex, deviceDOM in enumerate(deviceDOMList):
blockList = ''
for block in paramClass.getParamBlocks():
for block in paramClass.getParamMemoryBlocks():
blockList += s7template.stlBlock(paramClass.name, block.name)
deviceLabel = deviceDOM.prop('label')
stlString += s7template.generateBlock(param.owner, paramClass.name, paramClass.version, param.controller.system, DBnumber, deviceLabel, blockList, (deviceIndex == 0),param.controller.protocol)
fesaLabel = deviceDOM.prop('fesa-label')
stlString += s7template.generateBlock(param.owner, paramClass.name, paramClass.version, param.controller.system, DBnumber, deviceLabel, blockList, (deviceIndex == 0), param.controller.protocol, fesaLabel)
symString += s7template.symDeviceDB(paramClass.name, paramClass.version, deviceLabel, DBnumber, (deviceIndex == 0))
DBnumber += 1
else: # BLOCK_MODE
# Generate the Data-Blocks: one DB per Block of registers
for blockIndex, block in enumerate(paramClass.getParamBlocks()):
for blockIndex, block in enumerate(paramClass.getParamMemoryBlocks()):
deviceList = ''
for deviceDOM in deviceDOMList:
deviceLabel = deviceDOM.prop('label')
deviceList += s7template.stlDevice(deviceLabel, paramClass.name, block.name)
fesaLabel = deviceDOM.prop('fesa-label')
deviceList += s7template.stlDevice(deviceLabel, paramClass.name, block.name, fesaLabel)
stlString += s7template.generateBlock(param.owner, paramClass.name, paramClass.version, param.controller.system, DBnumber, block.name, deviceList, (blockIndex == 0),param.controller.protocol)
stlString += s7template.generateBlock(param.owner, paramClass.name, paramClass.version, param.controller.system, DBnumber, block.name, deviceList, (blockIndex == 0),param.controller.protocol, "")
symString += s7template.symBlockDB(paramClass.name, paramClass.version, block.name, DBnumber, (blockIndex == 0))
DBnumber += 1
......@@ -204,7 +219,7 @@ def generateSchneiderRegisters(xsydoc, paramClass, deviceDOM, block, deviceIndex
#Device and Block adresses come from the generated Paramerers file. Depends on the protocol mode:
#DEVICE_MODE: relative address of the element within it parent node
#BLOCK_MODE : absolute address in the entire PLC memory
deviceAddress = long(deviceDOM.prop('address'))
deviceAddress = int(deviceDOM.prop('address'))
for register in block.getParamRegisters():
# If PLC does not supports this register format abort this generation and log the error
......@@ -222,7 +237,7 @@ def generateSchneiderRegisters(xsydoc, paramClass, deviceDOM, block, deviceIndex
dataElt = xsydoc.xpathEval("//dataBlock")[0] #only one dataBlock node has been inserted so far
regElt = libxml2.newNode('variables')
# Compute the base checksum of the class name
classNameCRC = zlib.crc32(paramClass.name,0) & 0xffff
classNameCRC = zlib.crc32((paramClass.name).encode(),0) & 0xffff
# Create register name = regname_crc32(classname)_deviceid
# 24 char (register name) + 1 (underscore) + 4 char (CRC classname) + 1 (underscore) + n (device id) = 30 + n (device id)
# possible problem when device id (n)> 99 --> string > 32 not compatible with Schneider PLCs
......@@ -240,7 +255,7 @@ def generateSchneiderRegisters(xsydoc, paramClass, deviceDOM, block, deviceIndex
if register.name == '_version': initElt.setProp("value", param.silecsVersion )
if register.name == '_user' : initElt.setProp("value", param.owner)
if register.name == '_checksum': initElt.setProp("value", "%s" %param.checksum)
if register.name == '_date' : initElt.setProp("value", getDateTime())
if register.name == '_date' : initElt.setProp("value", getDateTime(param.controller))
regElt.addChild(initElt)
commElt = libxml2.newNode('comment')
commElt.setContent(paramClass.name+"/"+deviceLabel+"/"+block.name)
......@@ -249,9 +264,6 @@ def generateSchneiderRegisters(xsydoc, paramClass, deviceDOM, block, deviceIndex
# ----------------------------------------------------
def generateSchneiderSources(param,sourceFolderPath,logTopics):
#Base address provided by the user in the Deployment relies on the PLC model:
#Quantum, Premium and M340 Schneider PLCs are all interpreted as using 16-bit addressing,
#even though M340 addresses are later properly considered as 32-bit addresses
# Create the Schneider DOM source (Unity <.XSY> XML document)
xsyDoc = libxml2.newDoc(version='1.0')
......@@ -263,7 +275,7 @@ def generateSchneiderSources(param,sourceFolderPath,logTopics):
for paramClass in param.controller.paramClasses:
deviceDOMList = paramClass.getDeviceInstanceNodes() # get Device instances of that class
for block in paramClass.getParamBlocks():
for block in paramClass.getParamMemoryBlocks():
for deviceIndex,deviceDOM in enumerate(deviceDOMList):
if param.controller.protocol == 'DEVICE_MODE': #-------------------
generateSchneiderRegisters(xsyDoc, paramClass, deviceDOM, block, deviceIndex, True, param, logTopics)
......@@ -288,7 +300,7 @@ def generateDIGISources(param,sourceFolderPath,logTopics):
for paramClass in param.controller.paramClasses:
cTypeDefinitions += DIGITemplate.cFirstBlockUDT(paramClass.name, paramClass.version)
for block in paramClass.getParamBlocks():
for block in paramClass.getParamMemoryBlocks():
registerList = ''
for register in block.getParamRegisters():
if register.format == 'float64':
......@@ -304,7 +316,7 @@ def generateDIGISources(param,sourceFolderPath,logTopics):
if param.controller.protocol == 'DEVICE_MODE':
# Generate the List of block in the class
blockList =''
for block in paramClass.getParamBlocks():
for block in paramClass.getParamMemoryBlocks():
blockList += DIGITemplate.cDeviceModeBlockInstantiation(paramClass.name, block.name)
deviceList = ''
......@@ -314,7 +326,7 @@ def generateDIGISources(param,sourceFolderPath,logTopics):
deviceList = deviceList[:-2] # remove last comma space
classList += DIGITemplate.cDeviceModeClass_deviceList(blockList, deviceList)
else: # BLOCK_MODE
for block in paramClass.getParamBlocks():
for block in paramClass.getParamMemoryBlocks():
# DeviceList
deviceList = ''
for deviceDOM in paramClass.getDeviceInstanceNodes():
......@@ -379,7 +391,7 @@ def generateVirtualS7Sources(param,sourceFolderPath,logTopics):
getDesign += virtualS7Template.vs7DuDesignGet(paramClass.name, paramClass.version)
#CLASSES code generation ================================
for block in paramClass.getParamBlocks():
for block in paramClass.getParamMemoryBlocks():
getSetCodeString = ''
dimsCodeString = ''
dataCodeString = ''
......@@ -453,7 +465,7 @@ def generateBeckhoffRegisters(param, paramClass, deviceDOM, block, deviceIndex,
#Device and Block adresses come from the generated Paramerers file. Depends on the protocol mode:
#DEVICE_MODE: relative address of the element within it parent node
#BLOCK_MODE : absolute address in the entire PLC memory
deviceAddress = long(deviceDOM.prop('address'))
deviceAddress = int(deviceDOM.prop('address'))
for register in block.getParamRegisters():
# If PLC does not supports this register format abort this generation and log the error
......@@ -466,9 +478,9 @@ def generateBeckhoffRegisters(param, paramClass, deviceDOM, block, deviceIndex,
# Compute the register address relying on the Class Parameters file data
# Attention! Beckhoff uses WORD addressing while Parameters data are expressed in bytes to be PLC independent
if param.controller.model == 'BC9020':
totalAddress = (block.address - long(param.controller.address[MEM_TYPE]) + (deviceIndex * block.memSize) + register.address)/2
totalAddress = (block.address - int(param.controller.address[MEM_TYPE]) + (deviceIndex * block.memSize) + register.address)/2
elif param.controller.model == 'CX9020':
totalAddress = (block.address - long(param.controller.address[MEM_TYPE]) + (deviceIndex * block.memSize) + register.address)
totalAddress = (block.address - int(param.controller.address[MEM_TYPE]) + (deviceIndex * block.memSize) + register.address)
else:
raise "PLC model not supported: " + param.controller.model
......@@ -476,17 +488,17 @@ def generateBeckhoffRegisters(param, paramClass, deviceDOM, block, deviceIndex,
source += ' (*'+paramClass.name+'/'+deviceLabel+'/'+block.name+' *)\r\n'
# Compute the base checksum of the class name
classNameCRC = zlib.crc32(paramClass.name,0) & 0xffff
classNameCRC = zlib.crc32((paramClass.name).encode(),0) & 0xffff
if block.name == 'hdrBlk':
if register.name == '_version':
source+=beckhoffRegInit %(register.name, classNameCRC, deviceLabel, totalAddress, whichTwincatFormat[register.format]+"(16)", param.silecsVersion )
source+=beckhoffRegInit %(register.name, classNameCRC, deviceLabel, int(totalAddress), whichTwincatFormat[register.format]+"(16)", param.silecsVersion )
if register.name == '_user':
source+=beckhoffRegInit %(register.name, classNameCRC, deviceLabel, totalAddress, whichTwincatFormat[register.format]+"(16)", "'"+param.owner+"'")
source+=beckhoffRegInit %(register.name, classNameCRC, deviceLabel, int(totalAddress), whichTwincatFormat[register.format]+"(16)", "'"+param.owner+"'")
if register.name == '_checksum':
source+=beckhoffRegInit %(register.name, classNameCRC, deviceLabel, totalAddress, whichTwincatFormat[register.format], param.checksum)
source+=beckhoffRegInit %(register.name, classNameCRC, deviceLabel, int(totalAddress), whichTwincatFormat[register.format], param.checksum)
if register.name == '_date':
source+=beckhoffRegInit %(register.name, classNameCRC, deviceLabel, totalAddress, whichTwincatFormat[register.format], getDateTime())
source+=beckhoffRegInit %(register.name, classNameCRC, deviceLabel, int(totalAddress), whichTwincatFormat[register.format], getDateTime(param.controller))
else: # not header block
# set data type - for string with fixed value registerLength
......@@ -496,11 +508,11 @@ def generateBeckhoffRegisters(param, paramClass, deviceDOM, block, deviceIndex,
format = whichTwincatFormat[register.format]
if register.dim1 == 1 and register.dim2 == 1: # scalar
source += beckhoffReg %(register.name, classNameCRC, deviceLabel, totalAddress, format)
source += beckhoffReg %(register.name, classNameCRC, deviceLabel, int(totalAddress), format)
elif register.dim1 > 1 and register.dim2 == 1: # array
source += beckhoffRegArray %(register.name, classNameCRC, deviceLabel, totalAddress, register.dim1-1, format)
source += beckhoffRegArray %(register.name, classNameCRC, deviceLabel, int(totalAddress), register.dim1-1, format)
else: # dim1>=1 and for whatever dim2, use double array syntax
source += beckhoffRegArray2d %(register.name, classNameCRC, deviceLabel, totalAddress, register.dim1-1, register.dim2-1, format)
source += beckhoffRegArray2d %(register.name, classNameCRC, deviceLabel, int(totalAddress), register.dim1-1, register.dim2-1, format)
return source
# ----------------------------------------------------
......@@ -511,6 +523,8 @@ def generateBeckhoffSources(param, sourceFolderPath, logTopics):
source += """* | C.E.R.N Geneva, Switzerland\r\n"""
source += """* | SILECS - BE/CO-FE\r\n"""
source += """* | April 2015\r\n"""
source += """* | This file is auto generated by the SILECS framework tools.\r\n"""
source += """* | Code regeneration will overwrite it.\r\n"""
source += """* +-------------------------------------------------------------------\r\n"""
source += """*\r\n"""
source += """* Release : SILECS_%s\r\n"""%(param.silecsVersion)
......@@ -522,14 +536,14 @@ def generateBeckhoffSources(param, sourceFolderPath, logTopics):
deviceDOMList = paramClass.getDeviceInstanceNodes() # get Device instances of that class
# Device mode is not supported for Beckhoff PLCs - Only block mode available
for block in paramClass.getParamBlocks():
for block in paramClass.getParamMemoryBlocks():
for deviceIndex, deviceDOM in enumerate(deviceDOMList):
source += generateBeckhoffRegisters(param, paramClass, deviceDOM, block, deviceIndex,logTopics)
source += 'END_VAR'
# Write the source into the EXP source file
generateControllerFiles(sourceFolderPath,param.controller.hostName,".exp",source,logTopics);
generateControllerFiles(sourceFolderPath,param.controller.hostName,".exp",source,logTopics)
#-------------------------------------------------------------------------
# NI code generation
......@@ -542,7 +556,7 @@ def generateControllerFiles(sourceFolderPath,hostName,fileExtention,source,logTo
fileName = hostName + fileExtention
fullFilePath = os.path.normpath(sourceFolderPath + "/" + fileName )
iecommon.logInfo("Generate PLC sources: %s" %fullFilePath,logTopics);
iecommon.logInfo("Generate PLC sources: %s" %fullFilePath,logTopics)
fdesc = open(fullFilePath, "w")
fdesc.write(source)
fdesc.close()
......
......@@ -20,7 +20,6 @@ import os
import sys
import getpass
import codecs
import commands
import iecommon
import libxml2
......@@ -38,7 +37,7 @@ def logError(msg, exit,logTopics={}):
if 'errorlog' in logTopics:
if logTopics['errorlog'] == True:
_logMsg = "%s%s" %(logHeader("ERROR"), msg)
print _logMsg
print(_logMsg)
if exit:
raise Exception(_logMsg)
......@@ -46,13 +45,13 @@ def logInfo(msg,logTopics={}):
if 'infolog' in logTopics:
if logTopics['infolog'] == True:
_logMsg = "%s%s" %(logHeader("INFO"), msg)
print _logMsg
print(_logMsg)
def logDebug(msg,logTopics={}):
if 'debuglog' in logTopics:
if logTopics['debuglog'] == True:
_logMsg = "%s%s" %(logHeader("DEBUG"), msg)
print _logMsg
print(_logMsg)
# Append message to a text file
def logToFile(path,msg):
......@@ -143,7 +142,7 @@ def getOrCreateNamedFirstChild(parent,elementName, attributeNameValue):
return newNode
def fillAttributes(element, attrs):
for name,value in attrs.iteritems():
for name,value in attrs.items():
if not type(value) is str:
raise Exception("Error: Wrong Type Parsed for attribute: " + name)
element.setProp(name, value)
......@@ -173,4 +172,9 @@ def getPatchSilecsVersion(silecsVersionString):
secondDot = silecsVersionString.find('.',firstDot)
return silecsVersionString[secondDot:]
def supportsDateTimeLong(controller):
if controller.brand != "SIEMENS":
return False
if controller.model in ['SIMATIC_S7-300', 'SIMATIC_S7-400']:
return False
return True
......@@ -26,9 +26,9 @@ designFormat = '.silecsdesign'
deployFormat = '.silecsdeploy'
paramFormat = '.silecsparam'
def loadSilecsDesignDOM(workspacePath, silecsDesign, silecsVersion, funcGetSilecsDesignFilePath):
def loadSilecsDesignDOM(workspacePath, silecsDesign, silecsVersion, funcGetSilecsDesignFilePath, datetime_format=xmltemplate.DATE_AND_TIME):
if silecsDesign.name == "SilecsHeader":
silecsHeader = xmltemplate.getSilecsHeader(silecsVersion)
silecsHeader = xmltemplate.getSilecsHeader(silecsVersion, datetime_format)
return libxml2.parseDoc(silecsHeader)
else:
designPath = funcGetSilecsDesignFilePath(workspacePath, silecsDesign.name)
......
......@@ -16,6 +16,7 @@
import os
import sys
import ParseMigrationArgs
from migrationBase import MigrationBase
from migration0_10_0to1_0_0.migrators import *
......@@ -26,8 +27,8 @@ import FileUtils
import shutil
class Migration(MigrationBase):
def __init__(self, arguments):
super(Migration, self).__init__()
def __init__(self, silecsDocument, xmlSchema, versionOld, versionNew, createBackup):
super(Migration, self).__init__(silecsDocument, xmlSchema, versionOld, versionNew, createBackup)
def migrateClass(self, context, projectDir ):
modified = designGenerateFesaPropValueItemMigrator(context)
......@@ -42,9 +43,7 @@ class Migration(MigrationBase):
return modified
def migrateFESAInstanceFile(self):
results = self.parser.parse_args()
silecsDocument = results.silecsDocument
projectDir = FileUtils.getProjectDir(silecsDocument)
projectDir = FileUtils.getProjectDir(self.silecsDocument)
testFolder = projectDir + "/src/test"
if not os.path.isdir(testFolder):
return
......@@ -60,7 +59,18 @@ class Migration(MigrationBase):
if fesaInstanceFileMigrator(context):
self._saveAndBackupFile(context,instanceFile)
if __name__ == "__main__":
migration = Migration(sys.argv)
def main_parse():
arguments = ParseMigrationArgs.parse_arguments()
run_migrate(arguments.silecsDocument,
arguments.xmlSchema,
arguments.versionOld,
arguments.versionNew,
arguments.createBackup)
def run_migrate(silecsDocument, xmlSchema, versionOld, versionNew, createBackup):
migration = Migration(silecsDocument, xmlSchema, versionOld, versionNew, createBackup)
migration.migrate()
migration.backupOldFESAMakeSpecific()
if __name__ == "__main__":
main_parse()
......@@ -16,6 +16,7 @@
import os
import sys
import ParseMigrationArgs
from migrationBase import MigrationBase
from migration_0_9_0to0_10_0.migrateDeployDeviceNumber import DeployDeviceNumberMigrator
......@@ -25,8 +26,8 @@ import libxml2
import sys
class Migration(MigrationBase):
def __init__(self, arguments):
super(Migration, self).__init__()
def __init__(self, silecsDocument, xmlSchema, versionOld, versionNew, createBackup):
super(Migration, self).__init__(silecsDocument, xmlSchema, versionOld, versionNew, createBackup)
self._deployDomainMigrator = DeployDomainMigrator()
self._migrateDeployDeviceNumber = DeployDeviceNumberMigrator()
......@@ -39,6 +40,17 @@ class Migration(MigrationBase):
modified = self._migrateDeployDeviceNumber.migrate(context)
return modified
if __name__ == "__main__":
migration = Migration(sys.argv)
def main_parse():
arguments = ParseMigrationArgs.parse_arguments()
run_migrate(arguments.silecsDocument,
arguments.xmlSchema,
arguments.versionOld,
arguments.versionNew,
arguments.createBackup)
def run_migrate(silecsDocument, xmlSchema, versionOld, versionNew, createBackup):
migration = Migration(silecsDocument, xmlSchema, versionOld, versionNew, createBackup)
migration.migrate()
if __name__ == "__main__":
main_parse()
......@@ -16,6 +16,7 @@
import os
import sys
import ParseMigrationArgs
from migrationBase import MigrationBase
from migration1_0_Xto2_0_0.migrators import *
......@@ -26,16 +27,26 @@ import FileUtils
import shutil
class Migration(MigrationBase):
def __init__(self, arguments):
super(Migration, self).__init__()
def __init__(self, silecsDocument, xmlSchema, versionOld, versionNew, createBackup):
super(Migration, self).__init__(silecsDocument, xmlSchema, versionOld, versionNew, createBackup)
def migrateClass(self, context, projectDir ):
def migrateClass(self, context, projectDir):
modified = designValueTypeMigrator(context)
modified |= designBlockRegisterMigrator(context)
return modified
def main_parse():
arguments = ParseMigrationArgs.parse_arguments()
run_migrate(arguments.silecsDocument,
arguments.xmlSchema,
arguments.versionOld,
arguments.versionNew,
arguments.createBackup)
if __name__ == "__main__":
migration = Migration(sys.argv)
def run_migrate(silecsDocument, xmlSchema, versionOld, versionNew, createBackup):
migration = Migration(silecsDocument, xmlSchema, versionOld, versionNew, createBackup)
migration.migrate()
migration.backupOldFESAMakeSpecific()
if __name__ == "__main__":
main_parse()
......@@ -16,6 +16,7 @@
import os
import sys
import ParseMigrationArgs
from migrationBase import MigrationBase
from migration2_0_Xto2_1_X.migrators import *
......@@ -26,11 +27,11 @@ import FileUtils
import shutil
class Migration(MigrationBase):
def __init__(self, arguments):
super(Migration, self).__init__()
def __init__(self, silecsDocument, xmlSchema, versionOld, versionNew, createBackup):
super(Migration, self).__init__(silecsDocument, xmlSchema, versionOld, versionNew, createBackup)
def migrateClass(self, context, projectDir ):
modified = false
modified = False
# fix reverted, since FESA does not allow to have code outside of the "src" folder
#modified = fesaClassIncludeHeaderMigrator(context,self.silecsDocument)
......@@ -52,8 +53,17 @@ class Migration(MigrationBase):
self.updateFESAMakeSpecific()
return modified
def main_parse():
arguments = ParseMigrationArgs.parse_arguments()
run_migrate(arguments.silecsDocument,
arguments.xmlSchema,
arguments.versionOld,
arguments.versionNew,
arguments.createBackup)
if __name__ == "__main__":
migration = Migration(sys.argv)
def run_migrate(silecsDocument, xmlSchema, versionOld, versionNew, createBackup):
migration = Migration(silecsDocument, xmlSchema, versionOld, versionNew, createBackup)
migration.migrate()
if __name__ == "__main__":
main_parse()
......@@ -50,13 +50,6 @@ def getFesaSourceFiles(fesaClassName, projectDir):
for root, subdirs, files in os.walk(fesaCodeFolder):
for file in files:
if file.endswith(".cpp") or file.endswith(".h"):
print os.path.join(root,file)
print(os.path.join(root,file))
sourceFiles.append(os.path.join(root,file))
return sourceFiles
def replaceInFile(filePath,searchString,replaceWithString):
with open(filePath, 'r') as file :
filedata = file.read()
filedata = filedata.replace(searchString, replaceWithString)
with open(filePath, 'w') as file:
file.write(filedata)
\ No newline at end of file
from argparse import ArgumentParser
def parse_arguments():
parser = ArgumentParser(description='Migration script')
parser.add_argument("silecsDocument", action="store", help="The SILECS document to migrate")
parser.add_argument("xmlSchema", action="store", help="Path to the new schema")
parser.add_argument("versionOld", action="store", help="old silecs version")
parser.add_argument("versionNew", action="store", help="new silecs version")
parser.add_argument('--backup', dest='createBackup', action='store_true')
parser.add_argument('--no-backup', dest='createBackup', action='store_false')
parser.set_defaults(createBackup=False)
arguments = parser.parse_args()
return arguments
......@@ -14,12 +14,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from test.testBase import *
import unittest
import libxml2
#from migration.0_10_0to1_0_0 import *
from migration.migration0_10_0to1_0_0.migrators import *
import inspect #get caller name
SilecsDesignOld = '''<?xml version="1.0" encoding="UTF-8"?>
<SILECS-Design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
......@@ -90,59 +88,50 @@ SilecsDeployOldParsed = libxml2.parseDoc(SilecsDeployOld)
fesaInstanceOldParsed = libxml2.parseFile("migration/migration0_10_0to1_0_0/DeviceData_PneuDriveDU.instance")
def testdeployRemoveSilecsHeaderMigrator(deployDoc):
deployRemoveSilecsHeaderMigrator(deployDoc)
silecsHeaders = deployDoc.xpathEval('/SILECS-Deploy/Deploy-Classes/Class/name[text()="SilecsHeader"]')
assertEqual(len(silecsHeaders),0)
class TestMigration0_10_0to1_0_0(unittest.TestCase):
def testdeployReOrderControllerAndClassesMigrator(deployDoc):
deployReOrderControllerAndClassesMigrator(deployDoc)
print deployDoc
oldClasses = deployDoc.xpathEval('/SILECS-Deploy/Deploy-Classes/Class')
assertEqual(len(oldClasses),0)
oldDeployInstances = deployDoc.xpathEval('/SILECS-Deploy/Deploy-Instances')
assertEqual(len(oldDeployInstances),0)
oldPLCNode = deployDoc.xpathEval('/SILECS-Deploy/Deploy-Unit/Siemens-PLC')
assertEqual(len(oldPLCNode),0)
def test_deploySwapStep7TiaMigrator(self):
deploySwapStep7TiaMigrator(SilecsDeployOldParsed)
plcEntries = SilecsDeployOldParsed.xpathEval("/SILECS-Deploy/Deploy-Unit/Siemens-PLC[@system='TIA-PORTAL']")
self.assertEqual(len(plcEntries),1)
newContollers = deployDoc.xpathEval('/SILECS-Deploy/Controller')
assertEqual(len(newContollers),2)
for newController in newContollers:
assertTrue(newController.hasProp("host-name"))
plcNodes = newController.xpathEval('Siemens-PLC')
assertEqual(len(plcNodes),1)
classNodes = newController.xpathEval('SilecsDesign')
assertEqual(len(classNodes),2)
pneuDrive = newController.xpathEval("SilecsDesign[@silecs-design-name='PneuDrive']")[0]
pneuDriveDevices = pneuDrive.xpathEval('Device')
assertEqual(len(pneuDriveDevices),6)
whatever = newController.xpathEval('SilecsDesign[@silecs-design-name="Whatever"]')[0]
whateverDevices = whatever.xpathEval('Device')
assertEqual(len(whateverDevices),1)
deployRemoveSilecsHeaderMigrator(SilecsDeployOldParsed)
silecsHeaders = SilecsDeployOldParsed.xpathEval('/SILECS-Deploy/Deploy-Classes/Class/name[text()="SilecsHeader"]')
self.assertEqual(len(silecsHeaders), 0)
def testdeploySwapStep7TiaMigrator(deployDoc):
deploySwapStep7TiaMigrator(deployDoc)
plcEntries = deployDoc.xpathEval("/SILECS-Deploy/Deploy-Unit/Siemens-PLC[@system='TIA-PORTAL']")
assertEqual(len(plcEntries),1)
deployReOrderControllerAndClassesMigrator(SilecsDeployOldParsed)
oldClasses = SilecsDeployOldParsed.xpathEval('/SILECS-Deploy/Deploy-Classes/Class')
self.assertEqual(len(oldClasses),0)
oldDeployInstances = SilecsDeployOldParsed.xpathEval('/SILECS-Deploy/Deploy-Instances')
self.assertEqual(len(oldDeployInstances),0)
oldPLCNode = SilecsDeployOldParsed.xpathEval('/SILECS-Deploy/Deploy-Unit/Siemens-PLC')
self.assertEqual(len(oldPLCNode),0)
def testdesignGenerateFesaPropValueItemMigrator(context):
designGenerateFesaPropValueItemMigrator(context)
updatedBlocks = context.xpathEval("//Block[@generateFesaProperty='true']")
updatedRegisters = context.xpathEval("//Register[@generateFesaValueItem='true']")
assertEqual(len(updatedBlocks),2)
assertEqual(len(updatedRegisters),12)
newContollers = SilecsDeployOldParsed.xpathEval('/SILECS-Deploy/Controller')
self.assertEqual(len(newContollers),2)
for newController in newContollers:
self.assertTrue(newController.hasProp("host-name"))
plcNodes = newController.xpathEval('Siemens-PLC')
self.assertEqual(len(plcNodes),1)
classNodes = newController.xpathEval('SilecsDesign')
self.assertEqual(len(classNodes),2)
pneuDrive = newController.xpathEval("SilecsDesign[@silecs-design-name='PneuDrive']")[0]
pneuDriveDevices = pneuDrive.xpathEval('Device')
self.assertEqual(len(pneuDriveDevices),6)
whatever = newController.xpathEval('SilecsDesign[@silecs-design-name="Whatever"]')[0]
whateverDevices = whatever.xpathEval('Device')
self.assertEqual(len(whateverDevices),1)
def testfesaInstanceFileMigrator(context):
fesaInstanceFileMigrator(context)
newValues = context.xpathEval("//device-instance[@name='YR11DF3']/configuration/parameterFile/value")
assertEqual(len(newValues),1)
assertEqual(newValues[0].getContent(),"../../../generated/client/sdaplc003.silecsparam")
def runTests():
testdeploySwapStep7TiaMigrator(SilecsDeployOldParsed)
testdeployRemoveSilecsHeaderMigrator(SilecsDeployOldParsed)
testdeployReOrderControllerAndClassesMigrator(SilecsDeployOldParsed)
testdesignGenerateFesaPropValueItemMigrator(SilecsDesignOldParsed)
testfesaInstanceFileMigrator(fesaInstanceOldParsed)
def test_designGenerateFesaPropValueItemMigrator(self):
designGenerateFesaPropValueItemMigrator(SilecsDesignOldParsed)
updatedBlocks = SilecsDesignOldParsed.xpathEval("//Block[@generateFesaProperty='true']")
updatedRegisters = SilecsDesignOldParsed.xpathEval("//Register[@generateFesaValueItem='true']")
self.assertEqual(len(updatedBlocks),2)
self.assertEqual(len(updatedRegisters),12)
# print deployDoc # for debugging
def test_fesaInstanceFileMigrator(self):
fesaInstanceFileMigrator(fesaInstanceOldParsed)
newValues = fesaInstanceOldParsed.xpathEval("//device-instance[@name='YR11DF3']/configuration/parameterFile/value")
self.assertEqual(len(newValues),1)
self.assertEqual(newValues[0].getContent(),"../../../generated/client/sdaplc003.silecsparam")
#!/usr/bin/python
# Copyright 2016 CERN and GSI
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from test.testBase import *
import libxml2
from migration.migration1_0_Xto2_0_0.migrators import *
import inspect #get caller name
def testdesignValueTypeMigrator():
SilecsDesignOld = '''<?xml version="1.0" encoding="UTF-8"?>
<SILECS-Design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
silecs-version="0.10.0" created="03/21/16" updated="03/21/16"
xsi:noNamespaceSchemaLocation="/common/usr/cscofe/silecs/0.10.0/silecs-model/src/xml/DesignSchema.xsd">
<Information>
<Owner user-login="schwinn" />
<Editor user-login="schwinn" />
</Information>
<SILECS-Class name="PneuDrive" version="0.1.0" domain="TEST">
<Block name="Acq" mode="READ-ONLY">
<Register name="string1" synchro="MASTER" format="string" />
<Register name="string2" synchro="MASTER" format="string" string-len="65"/>
<Register name="string1D" synchro="MASTER" format="string" array-dim1="2" />
<Register name="string2D" synchro="MASTER" format="string" array-dim1="2" array-dim2="4"/>
<Register name="scalar" synchro="MASTER" format="int8" />
<Register name="scalar1D" synchro="MASTER" format="int8" array-dim1="2" />
<Register name="scalar2D" synchro="MASTER" format="int8" array-dim1="2" array-dim2="4"/>
</Block>
</SILECS-Class>
</SILECS-Design>'''
context = libxml2.parseDoc(SilecsDesignOld)
designValueTypeMigrator(context)
#context.shellPrintNode() #for debug
scalar = context.xpathEval("//Register[not(@format)]/scalar[@format='int8']")
array = context.xpathEval("//Register[not(@array-dim1)]/array[@dim='2' and @format='int8']")
array2D = context.xpathEval("//Register[not(@array-dim2)]/array2D[@dim1='2' and @dim2='4' and @format='int8']")
string1 = context.xpathEval("//Register/string[@format='string' and @string-length='64']")
string2 = context.xpathEval("//Register[not(@string-len)]/string[@format='string' and @string-length='65']")
stringArray = context.xpathEval("//Register/stringArray[@dim='2' and @format='string' and @string-length='64']")
stringArray2D = context.xpathEval("//Register/stringArray2D[@dim1='2' and @dim2='4' and @format='string' and @string-length='64']")
assertEqual(len(scalar),1)
assertEqual(len(array),1)
assertEqual(len(array2D),1)
assertEqual(len(string1),1)
assertEqual(len(string2),1)
assertEqual(len(stringArray),1)
assertEqual(len(stringArray2D),1)
def testdesignBlockRegisterMigrator():
SilecsDesignOld = '''<?xml version="1.0" encoding="UTF-8"?>
<SILECS-Design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
silecs-version="0.10.0" created="03/21/16" updated="03/21/16"
xsi:noNamespaceSchemaLocation="/common/usr/cscofe/silecs/0.10.0/silecs-model/src/xml/DesignSchema.xsd">
<Information>
<Owner user-login="schwinn" />
<Editor user-login="schwinn" />
</Information>
<SILECS-Class name="PneuDrive" version="0.1.0" domain="TEST">
<Block name="block1" mode="READ-ONLY">
<Register name="b1r1" synchro="MASTER" format="string" />
<Register name="b1r2" synchro="NONE" format="string" array-dim1="2" />
</Block>
<Block name="block2" mode="READ-WRITE">
<Register name="b2r1" synchro="MASTER" format="string" />
<Register name="b2r2" synchro="SLAVE" format="string" />
<Register name="b2r3" synchro="NONE" format="string" array-dim1="2" />
</Block>
<Block name="block3" mode="WRITE-ONLY">
<Register name="b3r1" synchro="SLAVE" format="string" />
<Register name="b3r2" synchro="NONE" format="string" array-dim1="2" />
</Block>
</SILECS-Class>
</SILECS-Design>'''
context = libxml2.parseDoc(SilecsDesignOld)
designBlockRegisterMigrator(context)
context.shellPrintNode() #for debug
oldBlocks = context.xpathEval("//Block")
oldRegisters = context.xpathEval("//Register")
assertEqual(len(oldBlocks),0)
assertEqual(len(oldRegisters),0)
block1 = context.xpathEval("/SILECS-Design/SILECS-Class/Acquisition-Block")
block2 = context.xpathEval("/SILECS-Design/SILECS-Class/Setting-Block")
block3 = context.xpathEval("/SILECS-Design/SILECS-Class/Command-Block")
assertEqual(len(block1),1)
assertEqual(len(block2),1)
assertEqual(len(block3),1)
b1r1 = context.xpathEval("/SILECS-Design/SILECS-Class/Acquisition-Block/Acquisition-Register[@name='b1r1']")
b1r2 = context.xpathEval("/SILECS-Design/SILECS-Class/Acquisition-Block/Acquisition-Register[@name='b1r2']")
b2r1 = context.xpathEval("/SILECS-Design/SILECS-Class/Setting-Block/Volatile-Register[@name='b2r1']")
b2r2 = context.xpathEval("/SILECS-Design/SILECS-Class/Setting-Block/Setting-Register[@name='b2r2']") # synchro = master ? --> Missconfiguration --> We should have this reg in an acquisition-block
b2r3 = context.xpathEval("/SILECS-Design/SILECS-Class/Setting-Block/Volatile-Register[@name='b2r3']")
b3r2 = context.xpathEval("/SILECS-Design/SILECS-Class/Command-Block/Setting-Register[@name='b3r1']")
b3r2 = context.xpathEval("/SILECS-Design/SILECS-Class/Command-Block/Setting-Register[@name='b3r2']")
def runTests():
testdesignValueTypeMigrator()
testdesignBlockRegisterMigrator()
# print deployDoc # for debugging
#!/usr/bin/python
# Copyright 2016 CERN and GSI
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
import libxml2
from migration.migration1_0_Xto2_0_0.migrators import *
class TestMigration1_0_Xto2_0_0(unittest.TestCase):
def test_designValueTypeMigrator(self):
SilecsDesignOld = '''<?xml version="1.0" encoding="UTF-8"?>
<SILECS-Design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
silecs-version="0.10.0" created="03/21/16" updated="03/21/16"
xsi:noNamespaceSchemaLocation="/common/usr/cscofe/silecs/0.10.0/silecs-model/src/xml/DesignSchema.xsd">
<Information>
<Owner user-login="schwinn" />
<Editor user-login="schwinn" />
</Information>
<SILECS-Class name="PneuDrive" version="0.1.0" domain="TEST">
<Block name="Acq" mode="READ-ONLY">
<Register name="string1" synchro="MASTER" format="string" />
<Register name="string2" synchro="MASTER" format="string" string-len="65"/>
<Register name="string1D" synchro="MASTER" format="string" array-dim1="2" />
<Register name="string2D" synchro="MASTER" format="string" array-dim1="2" array-dim2="4"/>
<Register name="scalar" synchro="MASTER" format="int8" />
<Register name="scalar1D" synchro="MASTER" format="int8" array-dim1="2" />
<Register name="scalar2D" synchro="MASTER" format="int8" array-dim1="2" array-dim2="4"/>
</Block>
</SILECS-Class>
</SILECS-Design>'''
context = libxml2.parseDoc(SilecsDesignOld)
designValueTypeMigrator(context)
#context.shellPrintNode() #for debug
scalar = context.xpathEval("//Register[not(@format)]/scalar[@format='int8']")
array = context.xpathEval("//Register[not(@array-dim1)]/array[@dim='2' and @format='int8']")
array2D = context.xpathEval("//Register[not(@array-dim2)]/array2D[@dim1='2' and @dim2='4' and @format='int8']")
string1 = context.xpathEval("//Register/string[@format='string' and @string-length='64']")
string2 = context.xpathEval("//Register[not(@string-len)]/string[@format='string' and @string-length='65']")
stringArray = context.xpathEval("//Register/stringArray[@dim='2' and @format='string' and @string-length='64']")
stringArray2D = context.xpathEval("//Register/stringArray2D[@dim1='2' and @dim2='4' and @format='string' and @string-length='64']")
self.assertEqual(len(scalar), 1)
self.assertEqual(len(array), 1)
self.assertEqual(len(array2D), 1)
self.assertEqual(len(string1), 1)
self.assertEqual(len(string2), 1)
self.assertEqual(len(stringArray), 1)
self.assertEqual(len(stringArray2D), 1)
def test_designBlockRegisterMigrator(self):
SilecsDesignOld = '''<?xml version="1.0" encoding="UTF-8"?>
<SILECS-Design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
silecs-version="0.10.0" created="03/21/16" updated="03/21/16"
xsi:noNamespaceSchemaLocation="/common/usr/cscofe/silecs/0.10.0/silecs-model/src/xml/DesignSchema.xsd">
<Information>
<Owner user-login="schwinn" />
<Editor user-login="schwinn" />
</Information>
<SILECS-Class name="PneuDrive" version="0.1.0" domain="TEST">
<Block name="block1" mode="READ-ONLY">
<Register name="b1r1" synchro="MASTER" format="string" />
<Register name="b1r2" synchro="NONE" format="string" array-dim1="2" />
</Block>
<Block name="block2" mode="READ-WRITE">
<Register name="b2r1" synchro="MASTER" format="string" />
<Register name="b2r2" synchro="SLAVE" format="string" />
<Register name="b2r3" synchro="NONE" format="string" array-dim1="2" />
</Block>
<Block name="block3" mode="WRITE-ONLY">
<Register name="b3r1" synchro="SLAVE" format="string" />
<Register name="b3r2" synchro="NONE" format="string" array-dim1="2" />
</Block>
</SILECS-Class>
</SILECS-Design>'''
context = libxml2.parseDoc(SilecsDesignOld)
designBlockRegisterMigrator(context)
context.shellPrintNode() #for debug
oldBlocks = context.xpathEval("//Block")
oldRegisters = context.xpathEval("//Register")
self.assertEqual(len(oldBlocks),0)
self.assertEqual(len(oldRegisters),0)
block1 = context.xpathEval("/SILECS-Design/SILECS-Class/Acquisition-Block")
block2 = context.xpathEval("/SILECS-Design/SILECS-Class/Setting-Block")
block3 = context.xpathEval("/SILECS-Design/SILECS-Class/Command-Block")
self.assertEqual(len(block1),1)
self.assertEqual(len(block2),1)
self.assertEqual(len(block3),1)
b1r1 = context.xpathEval("/SILECS-Design/SILECS-Class/Acquisition-Block/Acquisition-Register[@name='b1r1']")
b1r2 = context.xpathEval("/SILECS-Design/SILECS-Class/Acquisition-Block/Acquisition-Register[@name='b1r2']")
b2r1 = context.xpathEval("/SILECS-Design/SILECS-Class/Setting-Block/Volatile-Register[@name='b2r1']")
b2r2 = context.xpathEval("/SILECS-Design/SILECS-Class/Setting-Block/Setting-Register[@name='b2r2']") # synchro = master ? --> Missconfiguration --> We should have this reg in an acquisition-block
b2r3 = context.xpathEval("/SILECS-Design/SILECS-Class/Setting-Block/Volatile-Register[@name='b2r3']")
b3r2 = context.xpathEval("/SILECS-Design/SILECS-Class/Command-Block/Setting-Register[@name='b3r1']")
b3r2 = context.xpathEval("/SILECS-Design/SILECS-Class/Command-Block/Setting-Register[@name='b3r2']")
......@@ -105,7 +105,10 @@ def silecsDeployMigrator(silecsDocument):
silecsDesign.unlinkNode()
alreadyThere = silecsDocument.xpathEval("/SILECS-Deploy/SilecsDesign[@silecs-design-name='" + silecsDesign.prop("silecs-design-name") + "']")
if len(alreadyThere) == 0:
deployUnitNode.addNextSibling(silecsDesign)
newDesign = libxml2.newNode("SilecsDesign") #need to re-build, otherwise error for empty </SilecsDesign>
newDesign.newProp("silecs-design-version",silecsDesign.prop("silecs-design-version"))
newDesign.newProp("silecs-design-name",silecsDesign.prop("silecs-design-name"))
deployUnitNode.addNextSibling(newDesign)
modified = True
return modified
#!/usr/bin/python
# Copyright 2016 CERN and GSI
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from test.testBase import *
import libxml2
from migration.migration2_0_Xto2_1_X.migrators import *
import inspect # get caller name
from shutil import copyfile
import os
#-------------------------------------- def testFesaClassMakeSpecificMigrator():
#------------- currentDirectory = os.path.dirname(os.path.abspath(__file__))
# makefileSpecific = os.path.join(currentDirectory, "myProject", "Makefile.specific")
# makefileSpecificOriginal = os.path.join(currentDirectory, "myProject", "Makefile.specific.original")
# makefileSpecificCorrect = os.path.join(currentDirectory, "myProject", "Makefile.specific.correct")
#-------------------------------------- if os.path.isfile(makefileSpecific):
#------------------------------------------- os.remove(makefileSpecific)
#---------------------- copyfile(makefileSpecificOriginal, makefileSpecific)
#--------------------------- fesaClassMakeSpecificMigrator(makefileSpecific)
#----------------- assertFileEqual(makefileSpecific,makefileSpecificCorrect)
def testSilecsDeployMigrator():
silecsDeployDocOld = '''<?xml version="1.0" encoding="UTF-8"?>
<SILECS-Deploy silecs-version="1.0.1" created="05/03/17" updated="05/03/17" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="/common/usr/cscofe/silecs/silecs-model/1.0.1/xml/DeploySchema.xsd">
<Information>
<Owner user-login="schwinn" />
<Editor user-login="schwinn" />
</Information>
<Deploy-Unit name="AlexTestDU" version="0.1.0" />
<Controller host-name="asl733">
<Siemens-PLC system="TIA-PORTAL" model="SIMATIC_S7-300" protocol="DEVICE_MODE" base-DB-number="1" />
<SilecsDesign silecs-design-version="0.1.0" silecs-design-name="AlexTest1">
<Device device-name="myDev1" />
<Device device-name="myDev2" />
</SilecsDesign>
<SilecsDesign silecs-design-version="0.1.0" silecs-design-name="AlexTest2">
<Device device-name="myDev3" />
<Device device-name="myDev4" />
</SilecsDesign>
</Controller>
<Controller host-name="asl744">
<Siemens-PLC system="TIA-PORTAL" model="SIMATIC_S7-300" protocol="DEVICE_MODE" base-DB-number="1" />
<SilecsDesign silecs-design-version="0.1.0" silecs-design-name="AlexTest1">
<Device device-name="myDev1" />
<Device device-name="myDev2" />
</SilecsDesign>
<SilecsDesign silecs-design-version="0.1.0" silecs-design-name="AlexTest2">
<Device device-name="myDev3" />
<Device device-name="myDev4" />
</SilecsDesign>
</Controller>
</SILECS-Deploy>'''
context = libxml2.parseDoc(silecsDeployDocOld)
silecsDeployMigrator(context)
print context
silecsDesigns = context.xpathEval("/SILECS-Deploy/SilecsDesign")
assertEqual(len(silecsDesigns),2)
devices = context.xpathEval("/SILECS-Deploy/Controller[@host-name='asl733']/*/Device")
assertEqual(len(devices),4)
assertEqual(devices[0].prop("silecs-design-ref"),"AlexTest1")
assertEqual(devices[0].prop("silecs-device-label"),"myDev1")
def testremoveFesaConfigurationFieldParameterFile():
FesaDocOld = '''<?xml version="1.0" encoding="UTF-8"?>
<equipment-model xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="file:/opt/fesa/fesa-model-gsi/4.2.0/xml/design/design-gsi.xsd">
<data>
<device-data>
<configuration>
<field name="parameterFile" id="_170711152634_36"><description>ParameterFile of the PLC (*.silecsparam)</description><array type="char"><dim>512</dim></array><default>../../../generated/client/MyControllerName.silecsparam</default></field><field name="plcDeviceLabel" id="_170711152634_37"><description>Name of the related SILECS instance within the PLC mapping</description><array type="char"><dim>128</dim></array></field><field name="plcHostName" id="_170711152634_38"><description>Hostname of the PLC that contains the related SILECS class device</description><array type="char"><dim>128</dim></array></field><GSI-detailed-status-labels-field name="detailedStatus_labels" id="_170503152015_8">
<array2D type="char">
<custom-constant-dim1 constant-name-ref="DETAILED_STATUS_SIZE"/>
<custom-constant-dim2 constant-name-ref="MAX_DETAILED_STATUS_LABEL_LENGTH"/>
</array2D>
<default>{myStatusLabel1,myStatusLabel2}</default>
</GSI-detailed-status-labels-field>
<GSI-detailed-status-severity-field name="detailedStatus_severity" id="_170503152015_9">
<custom-type-array data-type-name-ref="DETAILED_STATUS_SEVERITY">
<custom-constant-dim constant-name-ref="DETAILED_STATUS_SIZE"/>
</custom-type-array>
<default>{INFO,INFO}</default>
</GSI-detailed-status-severity-field>
<GSI-module-status-labels-field name="moduleStatus_labels" id="_170503152015_10">
<array2D type="char">
<custom-constant-dim1 constant-name-ref="MODULE_STATUS_SIZE"/>
<custom-constant-dim2 constant-name-ref="MAX_MODULE_STATUS_LABEL_LENGTH"/>
</array2D>
<default>{myModule1,myModule2}</default>
</GSI-module-status-labels-field>
</configuration>
</device-data>
</data>
</equipment-model>
'''
context = libxml2.parseDoc(FesaDocOld)
removeFesaConfigurationFieldParameterFile(context)
paramFields = context.xpathEval("//field[@name='parameterFile']")
assertEqual(len(paramFields),0)
def runTests():
#testFesaClassMakeSpecificMigrator()
testremoveFesaConfigurationFieldParameterFile()
testSilecsDeployMigrator()
# print deployDoc # for debugging
#!/usr/bin/python
# Copyright 2016 CERN and GSI
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
import libxml2
from migration.migration2_0_Xto2_1_X.migrators import *
class TestMigration2_0_Xto2_1_X(unittest.TestCase):
def test_silecsDeployMigrator(self):
silecsDeployDocOld = '''<?xml version="1.0" encoding="UTF-8"?>
<SILECS-Deploy silecs-version="1.0.1" created="05/03/17" updated="05/03/17" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="/common/usr/cscofe/silecs/silecs-model/1.0.1/xml/DeploySchema.xsd">
<Information>
<Owner user-login="schwinn" />
<Editor user-login="schwinn" />
</Information>
<Deploy-Unit name="AlexTestDU" version="0.1.0" />
<Controller host-name="asl733">
<Siemens-PLC system="TIA-PORTAL" model="SIMATIC_S7-300" protocol="DEVICE_MODE" base-DB-number="1" />
<SilecsDesign silecs-design-version="0.1.0" silecs-design-name="AlexTest1">
<Device device-name="myDev1" />
<Device device-name="myDev2" />
</SilecsDesign>
<SilecsDesign silecs-design-version="0.1.0" silecs-design-name="AlexTest2">
<Device device-name="myDev3" />
<Device device-name="myDev4" />
</SilecsDesign>
</Controller>
<Controller host-name="asl744">
<Siemens-PLC system="TIA-PORTAL" model="SIMATIC_S7-300" protocol="DEVICE_MODE" base-DB-number="1" />
<SilecsDesign silecs-design-version="0.1.0" silecs-design-name="AlexTest1">
<Device device-name="myDev1" />
<Device device-name="myDev2" />
</SilecsDesign>
<SilecsDesign silecs-design-version="0.1.0" silecs-design-name="AlexTest2">
<Device device-name="myDev3" />
<Device device-name="myDev4" />
</SilecsDesign>
</Controller>
</SILECS-Deploy>'''
context = libxml2.parseDoc(silecsDeployDocOld)
silecsDeployMigrator(context)
print(context)
silecsDesigns = context.xpathEval("/SILECS-Deploy/SilecsDesign")
self.assertEqual(len(silecsDesigns), 2)
devices = context.xpathEval("/SILECS-Deploy/Controller[@host-name='asl733']/*/Device")
self.assertEqual(len(devices),4)
self.assertEqual(devices[0].prop("silecs-design-ref"), "AlexTest1")
self.assertEqual(devices[0].prop("silecs-device-label"), "myDev1")
def test_removeFesaConfigurationFieldParameterFile(self):
FesaDocOld = '''<?xml version="1.0" encoding="UTF-8"?>
<equipment-model xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="file:/opt/fesa/fesa-model-gsi/4.2.0/xml/design/design-gsi.xsd">
<data>
<device-data>
<configuration>
<field name="parameterFile" id="_170711152634_36"><description>ParameterFile of the PLC (*.silecsparam)</description><array type="char"><dim>512</dim></array><default>../../../generated/client/MyControllerName.silecsparam</default></field><field name="plcDeviceLabel" id="_170711152634_37"><description>Name of the related SILECS instance within the PLC mapping</description><array type="char"><dim>128</dim></array></field><field name="plcHostName" id="_170711152634_38"><description>Hostname of the PLC that contains the related SILECS class device</description><array type="char"><dim>128</dim></array></field><GSI-detailed-status-labels-field name="detailedStatus_labels" id="_170503152015_8">
<array2D type="char">
<custom-constant-dim1 constant-name-ref="DETAILED_STATUS_SIZE"/>
<custom-constant-dim2 constant-name-ref="MAX_DETAILED_STATUS_LABEL_LENGTH"/>
</array2D>
<default>{myStatusLabel1,myStatusLabel2}</default>
</GSI-detailed-status-labels-field>
<GSI-detailed-status-severity-field name="detailedStatus_severity" id="_170503152015_9">
<custom-type-array data-type-name-ref="DETAILED_STATUS_SEVERITY">
<custom-constant-dim constant-name-ref="DETAILED_STATUS_SIZE"/>
</custom-type-array>
<default>{INFO,INFO}</default>
</GSI-detailed-status-severity-field>
<GSI-module-status-labels-field name="moduleStatus_labels" id="_170503152015_10">
<array2D type="char">
<custom-constant-dim1 constant-name-ref="MODULE_STATUS_SIZE"/>
<custom-constant-dim2 constant-name-ref="MAX_MODULE_STATUS_LABEL_LENGTH"/>
</array2D>
<default>{myModule1,myModule2}</default>
</GSI-module-status-labels-field>
</configuration>
</device-data>
</data>
</equipment-model>
'''
context = libxml2.parseDoc(FesaDocOld)
removeFesaConfigurationFieldParameterFile(context)
paramFields = context.xpathEval("//field[@name='parameterFile']")
self.assertEqual(len(paramFields), 0)
\ No newline at end of file
......@@ -18,22 +18,17 @@ import fnmatch
import os
import sys
import libxml2
from argparse import ArgumentParser
import FileUtils
import ParseMigrationArgs
class MigrationBase(object):
def __init__(self):
self.parser = ArgumentParser(description='Migration script')
self.parser.add_argument("silecsDocument", action="store", help="The SILECS document to migrate")
self.parser.add_argument("xmlSchema", action="store", help="Path to the new schema")
self.parser.add_argument("versionOld", action="store", help="old silecs version")
self.parser.add_argument("versionNew", action="store", help="new silecs version")
results = self.parser.parse_args()
self.versionOld = results.versionOld
self.versionNew = results.versionNew
self.silecsDocument = results.silecsDocument
self.xmlSchema = results.xmlSchema
def __init__(self, silecsDocument, xmlSchema, versionOld, versionNew, createBackup=False):
self.versionOld = versionOld
self.versionNew = versionNew
self.silecsDocument = silecsDocument
self.xmlSchema = xmlSchema
self.createBackup = createBackup
def fixSilecsVersion(self, context):
root = context.xpathEval("/*")[0]
if root.prop("silecs-version") != self.versionOld:
......@@ -50,18 +45,33 @@ class MigrationBase(object):
projectDir = FileUtils.getProjectDir(self.silecsDocument)
makeSpecific = os.path.join(projectDir,"Makefile.specific")
FileUtils.backupFile(makeSpecific)
def updateFESAMakeSpecific(self):
projectDir = FileUtils.getProjectDir(self.silecsDocument)
makeSpecific = os.path.join(projectDir,"Makefile.specific")
print("Migration-Info: Old Version-Strings in '" + makeSpecific + "' will be replaced\n")
if os.path.isfile(makeSpecific):
oldComm = "silecs-communication-cpp/" + self.versionOld
newComm = "silecs-communication-cpp/" + self.versionNew
FileUtils.replaceInFile(makeSpecific,oldComm,newComm)
oldSnap7 = "snap7/" + self.versionOld
newSnap7 = "snap7/" + self.versionNew
FileUtils.replaceInFile(makeSpecific,oldSnap7,newSnap7)
print(f"Migration-Info: {makeSpecific} will be updated.\n")
if FileUtils.fileExists(makeSpecific):
included = False
with open(makeSpecific, 'r') as file:
filedata = file.read()
if "include Makefile.silecs" in filedata:
# Correct include already in file, we have nothing to do.
included = True
if not included:
lines = []
for line in filedata.split('\n'):
if ("SILECS" in line) or ("snap7" in line) or ("SNAP7" in line):
print(f"Removing line '{line}'")
else:
lines.append(line + '\n')
lines.insert(0, "include Makefile.silecs\n")
lines.insert(0, "# Include SILECS makefile\n")
with open(makeSpecific, 'w') as file:
for line in lines:
file.write(line)
def removeGenCode(self):
projectDir = FileUtils.getProjectDir(self.silecsDocument)
......@@ -100,11 +110,13 @@ class MigrationBase(object):
# Unknown
else:
raise IOError("Document type unknown: %r" % extension)
# only chaning the version or the schema does not count as modification --> no backup needed
self.fixSilecsVersion(context)
self.fixXMLScheman(context,self.xmlSchema)
if modified:
self.updateFESAMakeSpecific()
if modified and self.createBackup:
self._saveAndBackupFile(context, self.silecsDocument)
else:
self._saveFile(context, self.silecsDocument)
......@@ -134,14 +146,18 @@ class MigrationBase(object):
context.saveTo(fd)
class MigrationAny(MigrationBase):
def __init__(self, arguments):
super(MigrationAny, self).__init__()
def run_migrate(silecsDocument, xmlSchema, versionOld, versionNew, createBackup):
migration = MigrationBase(silecsDocument, xmlSchema, versionOld, versionNew, createBackup)
migration.migrate()
def main_parse():
arguments = ParseMigrationArgs.parse_arguments()
def dummy(self):
#doNothing
print "dummy"
run_migrate(arguments.silecsDocument,
arguments.xmlSchema,
arguments.versionOld,
arguments.versionNew,
arguments.createBackup)
if __name__ == "__main__":
migration = MigrationAny(sys.argv)
migration.migrate()
\ No newline at end of file
main_parse()
......@@ -14,13 +14,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from test.testBase import *
import unittest
import libxml2
from migration.migration_0_9_0to0_10_0.migrateDeployDomain import *
from migration.migration_0_9_0to0_10_0.migrateDeployDeviceNumber import *
import inspect #get caller name
SilecsDeployOld = '''<?xml version="1.0" encoding="UTF-8"?>
<SILECS-Deploy silecs-version="0.9.0" created="03/04/16" updated="03/04/16"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
......@@ -59,33 +58,32 @@ SilecsDeployOld = '''<?xml version="1.0" encoding="UTF-8"?>
</SILECS-Deploy>
'''
def testMigrateDeployDeviceNumber(deployDoc):
migrator = DeployDeviceNumberMigrator()
migrator.migrate(deployDoc)
deviceNumbers = deployDoc.xpathEval("/SILECS-Deploy/Deploy-Classes/Class/device-number")
assertEqual(len(deviceNumbers),0)
deviceLists = deployDoc.xpathEval("/SILECS-Deploy/Deploy-Classes/Class/device-list")
assertEqual(len(deviceLists),3)
newHeader = deployDoc.xpathEval('/SILECS-Deploy/Deploy-Classes/Class/device-list/Device[@label="SilecsHeader"]')
assertEqual(len(newHeader),1)
test123 = deployDoc.xpathEval('/SILECS-Deploy/Deploy-Classes/Class/name[text()="Test123"]')
assertEqual(len(test123),1)
class TestMigration0_9_0to0_10_0(unittest.TestCase):
deviceListOnFirstPosition = test123[0].xpathEval('../*')[0]
assertTrue(deviceListOnFirstPosition.get_name() == 'device-list')
def setUp(self):
self.deployDoc = libxml2.parseDoc(SilecsDeployOld)
genericDevices = test123[0].xpathEval('../device-list/Device')
assertEqual(len(genericDevices),5)
def test_migrateDeployDeviceNumber(self):
migrator = DeployDeviceNumberMigrator()
migrator.migrate(self.deployDoc)
deviceNumbers = self.deployDoc.xpathEval("/SILECS-Deploy/Deploy-Classes/Class/device-number")
self.assertEqual(len(deviceNumbers),0)
deviceLists = self.deployDoc.xpathEval("/SILECS-Deploy/Deploy-Classes/Class/device-list")
self.assertEqual(len(deviceLists),3)
newHeader = self.deployDoc.xpathEval('/SILECS-Deploy/Deploy-Classes/Class/device-list/Device[@label="SilecsHeader"]')
self.assertEqual(len(newHeader),1)
test123 = self.deployDoc.xpathEval('/SILECS-Deploy/Deploy-Classes/Class/name[text()="Test123"]')
self.assertEqual(len(test123),1)
def testMigrateDeployDomain(deployDoc):
migrator = DeployDomainMigrator()
migrator.migrate(deployDoc)
controllers = deployDoc.xpathEval("/SILECS-Deploy/Deploy-Instances/Controller")
for controller in controllers:
assertFalse(controller.hasProp('domain'))
deviceListOnFirstPosition = test123[0].xpathEval('../*')[0]
self.assertTrue(deviceListOnFirstPosition.get_name() == 'device-list')
def runTests():
deployDoc = libxml2.parseDoc(SilecsDeployOld)
testMigrateDeployDomain(deployDoc)
testMigrateDeployDeviceNumber(deployDoc)
# print deployDoc # for debugging
genericDevices = test123[0].xpathEval('../device-list/Device')
self.assertEqual(len(genericDevices),5)
def test_MigrateDeployDomain(self):
migrator = DeployDomainMigrator()
migrator.migrate(self.deployDoc)
controllers = self.deployDoc.xpathEval("/SILECS-Deploy/Deploy-Instances/Controller")
for controller in controllers:
self.assertFalse(controller.hasProp('domain'))