From 2408ddb9a1ffe3db6215830eeb090c3bf7848956 Mon Sep 17 00:00:00 2001
From: "m.marn" <m.marn@gsi.de>
Date: Mon, 6 Nov 2023 10:37:21 +0000
Subject: [PATCH] Add support for enums (#16)

Closes #16

See merge request silecs/opensilecs!57
---
 .../src/xml/fesa/fesa_3_0_0/fesaTemplates.py  | 113 +++++++++++++++
 .../xml/fesa/fesa_3_0_0/generateFesaDesign.py |  31 +++-
 .../xml/fesa/fesa_3_0_0/generateSourceCode.py |  19 +++
 .../xml/fesa/fesa_7_3_0/generateFesaDesign.py |  39 +++++
 silecs-codegen/src/xml/genduwrapper.py        |   4 +-
 .../src/xml/genduwrappertemplate.py           |   8 +-
 silecs-codegen/src/xml/genplcsrc.py           |   4 +-
 silecs-codegen/src/xml/model/Class/Block.py   |  19 +--
 silecs-codegen/src/xml/model/Class/Class.py   |  27 +++-
 .../src/xml/model/Class/Register.py           |  56 +++++--
 silecs-codegen/src/xml/model/Class/Types.py   |  25 ++++
 .../interface/equipment/SilecsRegister.cpp    |  26 +++-
 silecs-model/src/xml/DeploySchema.xsd         |   7 -
 .../src/xml/DesignSchema-custom-types.xsd     |  59 ++++++++
 .../src/xml/DesignSchema-data-types.xsd       | 137 ++++++++++++++++++
 silecs-model/src/xml/DesignSchema.xsd         | 101 ++-----------
 silecs-model/src/xml/shared.xsd               |  22 +++
 17 files changed, 570 insertions(+), 127 deletions(-)
 create mode 100644 silecs-codegen/src/xml/model/Class/Types.py
 create mode 100644 silecs-model/src/xml/DesignSchema-custom-types.xsd
 create mode 100644 silecs-model/src/xml/DesignSchema-data-types.xsd

diff --git a/silecs-codegen/src/xml/fesa/fesa_3_0_0/fesaTemplates.py b/silecs-codegen/src/xml/fesa/fesa_3_0_0/fesaTemplates.py
index 2818138..bece292 100644
--- a/silecs-codegen/src/xml/fesa/fesa_3_0_0/fesaTemplates.py
+++ b/silecs-codegen/src/xml/fesa/fesa_3_0_0/fesaTemplates.py
@@ -490,6 +490,9 @@ cGetStringArrayReg = string.Template("""
 
 cGetScalarReg = string.Template("""
         pDevice->${fesaFieldName}.set( pPLCDevice->getRegister("${regName}")->getVal<${regType}>(), pContext);""")
+
+cGetCustomScalarReg = string.Template("""
+        pDevice->${fesaFieldName}.set(static_cast<${enumName}::${enumName}>(pPLCDevice->getRegister("${regName}")->getVal<${regType}>()), pContext);""")
         
 cGetArrayReg = string.Template("""
         {
@@ -498,6 +501,19 @@ cGetArrayReg = string.Template("""
             pDevice->${fesaFieldName}.set(pRegister->getRefArray<${regType}>(dim1), dim1, pContext);
         }
         """)
+cGetCustomArrayReg = string.Template("""
+        {
+            auto& pRegister = pPLCDevice->getRegister("${regName}");
+            uint32_t dim1;
+            ${regType}* data = pRegister->getRefArray<${regType}>(dim1);
+            std::vector<${enumName}::${enumName}> ${regName};
+            for(uint32_t i = 0; i < dim1; ++i)
+            {
+                ${regName}.push_back(static_cast<${enumName}::${enumName}>(*(data + i)));
+            }
+            pDevice->${fesaFieldName}.set(${regName}.data(), dim1, pContext);
+        }
+        """)
 cGetArray2DReg = string.Template("""
         {
             auto& pRegister = pPLCDevice->getRegister("${regName}");
@@ -506,6 +522,20 @@ cGetArray2DReg = string.Template("""
             pDevice->${fesaFieldName}.set(pRegister->getRefArray2D<${regType}>(dim1, dim2), dim1, dim2, pContext);
         }
 """)
+cGetCustomArray2DReg = string.Template("""
+        {
+            auto& pRegister = pPLCDevice->getRegister("${regName}");
+            uint32_t dim1;
+            uint32_t dim2;
+            ${regType}* data = pRegister->getRefArray2D<${regType}>(dim1, dim2);
+            std::vector<${enumName}::${enumName}> ${regName};
+            for(uint32_t i = 0; i < dim1 * dim2; ++i)
+            {
+                ${regName}.push_back(static_cast<${enumName}::${enumName}>(*(data + i)));
+            }
+            pDevice->${fesaFieldName}.set(${regName}.data(), dim1, dim2, pContext);
+        }
+""")
 cGetUnsignedArray2DReg = string.Template("""
         {
             auto& pRegister = pPLCDevice->getRegister("${regName}");
@@ -577,6 +607,17 @@ cSetArrayReg = string.Template("""
         }
     """)
 
+cSetCustomArrayReg = string.Template("""
+        {
+            auto& pRegister = pPLCDevice->getRegister("${regName}");
+            uint32_t dim1 = pRegister->getDimension1();
+            uint32_t fesaDim1;
+            const ${enumName}::${enumName}* ${regName} = pDevice->${fesaFieldName}.get(fesaDim1${context});
+            std::vector<${regType}> data(${regName}, ${regName} + dim1);
+            pRegister->setValArray<${regType}>(data.data(), dim1);
+        }
+    """)
+
 cSetUnsignedArrayReg = string.Template("""
         {
             auto& pRegister = pPLCDevice->getRegister("${regName}");
@@ -597,6 +638,18 @@ cSetArray2DReg = string.Template("""
             pRegister->setValArray2D<${regType}>(pDevice->${fesaFieldName}.get(fesaDim1, fesaDim2${context}), dim1, dim2);
         }
 """)
+
+cSetCustomArray2DReg = string.Template("""
+        {
+            auto& pRegister = pPLCDevice->getRegister("${regName}");
+            uint32_t dim1 = pRegister->getDimension1();
+            uint32_t dim2 = pRegister->getDimension2();
+            uint32_t fesaDim1,fesaDim2;
+            const ${enumName}::${enumName}* ${regName} = pDevice->${fesaFieldName}.get(fesaDim1, fesaDim2${context});
+            std::vector<${regType}> data(${regName}, ${regName} + dim1 * dim2);
+            pRegister->setValArray2D<${regType}>(data.data(), dim1, dim2);
+        }
+""")
                                  
 cSetUnsignedArray2DReg = string.Template("""
         {
@@ -652,6 +705,19 @@ cSetUnsignedArrayRegData = string.Template("""
         }
     """)
 
+cSetCustomArrayRegData = string.Template("""
+        {
+            auto& pRegister = pPLCDevice->getRegister("${regName}");
+            uint32_t dim1 = pRegister->getDimension1();
+            uint32_t fesaDim1;
+            const ${enumName}::${enumName}* ${regName};
+            (data.is${fesaFieldName_upper}Available()) ? ${regName} = data.${fesaFieldName}.get(fesaDim1) :
+                                        ${regName} = pDevice->${fesaFieldName}.get(fesaDim1${context});
+            std::vector<${regType}> data(${regName}, ${regName} + dim1);
+            pRegister->setValArray<${regType}>(data.data(), dim1);
+        }
+    """)
+
 cSetArray2DRegData = string.Template("""
         {
             auto& pRegister = pPLCDevice->getRegister("${regName}");
@@ -677,6 +743,20 @@ cSetUnsignedArray2DRegData = string.Template("""
         }
 """)
 
+cSetCustomArray2DRegData = string.Template("""
+        {
+            auto& pRegister = pPLCDevice->getRegister("${regName}");
+            uint32_t dim1 = pRegister->getDimension1();
+            uint32_t dim2 = pRegister->getDimension2();
+            uint32_t fesaDim1,fesaDim2;
+            const ${enumName}::${enumName}* ${regName};
+            (data.is${fesaFieldName_upper}Available()) ? ${regName} = data.${fesaFieldName}.get(fesaDim1, fesaDim2) :
+                                        ${regName} = pDevice->${fesaFieldName}.get(fesaDim1, fesaDim2${context});
+            std::vector<${regType}> data(${regName}, ${regName} + dim1 * dim2);
+            pRegister->setValArray2D<${regType}>(data.data(), dim1, dim2);
+        }
+""")
+
 makeDesign = """# Include SILECS makefile
 include Makefile.silecs
 
@@ -797,6 +877,15 @@ def genCGetStringArrayReg(register):
 def genCGetScalarReg(register):
     return cGetScalarReg.substitute(regName=register.name, regType=register.getCType(), fesaFieldName=register.getFesaFieldName())
 
+def genCGetCustomScalarReg(register):
+    return cGetCustomScalarReg.substitute(regName=register.name, regType=register.getCType(), fesaFieldName=register.getFesaFieldName(), enumName=register.custom_type_name)
+
+def genCGetCustomArrayReg(register):
+    return cGetCustomArrayReg.substitute(regName=register.name, regType=register.getCType(), fesaFieldName=register.getFesaFieldName(), enumName=register.custom_type_name)
+
+def genCGetCustomArray2DReg(register):
+    return cGetCustomArray2DReg.substitute(regName=register.name, regType=register.getCType(), fesaFieldName=register.getFesaFieldName(), enumName=register.custom_type_name)
+
 def genCGetArrayReg(register):
     if register.isUnsigned():
         return cGetUnsignedArrayReg.substitute(regName=register.name, regType=register.getCType(), fesaFieldName=register.getFesaFieldName(), fesaType=register.getFesaType())
@@ -853,6 +942,18 @@ def genCSetArray2DReg(register):
         return cSetUnsignedArray2DReg.substitute(regName=register.name, regType=register.getCType(), fesaType=register.getFesaType(), fesaFieldName=register.getFesaFieldName(), context=context)
     else:
         return cSetArray2DReg.substitute(regName=register.name, regType=register.getCType(), fesaFieldName=register.getFesaFieldName(), context=context)
+
+def genCSetCustomArrayReg(register):
+    context = ", pContext"
+    if register.isConfiguration():
+        context = ""
+    return cSetCustomArrayReg.substitute(regName=register.name, regType=register.getCType(), fesaFieldName=register.getFesaFieldName(), context=context, enumName=register.custom_type_name)
+
+def genCSetCustomArray2DReg(register):
+    context = ", pContext"
+    if register.isConfiguration():
+        context = ""
+    return cSetCustomArray2DReg.substitute(regName=register.name, regType=register.getCType(), fesaFieldName=register.getFesaFieldName(), context=context, enumName=register.custom_type_name)
         
 def genCSetStringRegData(register):
     context = "pContext"
@@ -884,6 +985,12 @@ def genCSetArrayRegData(register):
     else:
         return cSetArrayRegData.substitute(regName=register.name, fesaFieldName_upper=register.getFesaFieldNameCapitalized(), regType=register.getCType(), fesaFieldName=register.getFesaFieldName(), context=context)
 
+def genCSetCustomArrayRegData(register):
+    context = ", pContext"
+    if register.isConfiguration():
+        context = ""
+    return cSetCustomArrayRegData.substitute(regName=register.name, fesaFieldName_upper=register.getFesaFieldNameCapitalized(), regType=register.getCType(), fesaType=register.getFesaType(), fesaFieldName=register.getFesaFieldName(), context=context, enumName=register.custom_type_name)
+
 def genCSetArray2DRegData(register):
     context = ", pContext"
     if register.isConfiguration():
@@ -893,6 +1000,12 @@ def genCSetArray2DRegData(register):
     else:
         return cSetArray2DRegData.substitute(regName=register.name, fesaFieldName_upper=register.getFesaFieldNameCapitalized(), regType=register.getCType(), fesaFieldName=register.getFesaFieldName(), context=context)
 
+def genCSetCustomArray2DRegData(register):
+    context = ", pContext"
+    if register.isConfiguration():
+        context = ""
+    return cSetCustomArray2DRegData.substitute(regName=register.name, fesaFieldName_upper=register.getFesaFieldNameCapitalized(), regType=register.getCType(), fesaType=register.getFesaType(), fesaFieldName=register.getFesaFieldName(), context=context, enumName=register.custom_type_name)
+
 def genMakeDesign():
     return makeDesign
 
diff --git a/silecs-codegen/src/xml/fesa/fesa_3_0_0/generateFesaDesign.py b/silecs-codegen/src/xml/fesa/fesa_3_0_0/generateFesaDesign.py
index 91f14a1..0265392 100644
--- a/silecs-codegen/src/xml/fesa/fesa_3_0_0/generateFesaDesign.py
+++ b/silecs-codegen/src/xml/fesa/fesa_3_0_0/generateFesaDesign.py
@@ -87,8 +87,29 @@ class FESADesignGenerator3_0_0(object):
         fillAttributes(scalarNode, {'type': register.getFesaType()})
         return scalarNode
 
-    def getOrCreateType(self,fieldNode,register):
-        if   register.valueType == "scalar":
+    def getOrCreateCustomScalarType(self, fieldNode, register):
+        scalarNode = getOrCreateChildElement(fieldNode,'custom-type-scalar')
+        fillAttributes(scalarNode, {'data-type-name-ref': register.custom_type_name})
+        return scalarNode
+
+    def getOrCreateCustomArrayType(self,fieldNode,register):
+        arrayNode = getOrCreateChildElement(fieldNode,'custom-type-array')
+        fillAttributes(arrayNode, {'data-type-name-ref': register.custom_type_name})
+        dimNode = getOrCreateChildElement(arrayNode,'dim')
+        dimNode.setContent(str(register.dim1))
+        return arrayNode
+    
+    def getOrCreateCustom2DArrayType(self,fieldNode,register):
+        array2DNode = getOrCreateChildElement(fieldNode,'custom-type-array2D')
+        fillAttributes(array2DNode, {'data-type-name-ref': register.custom_type_name})
+        dim1Node = getOrCreateChildElement(array2DNode,'dim1')
+        dim2Node = getOrCreateChildElement(array2DNode,'dim2')
+        dim1Node.setContent(str(register.dim1))
+        dim2Node.setContent(str(register.dim2))
+        return array2DNode
+
+    def getOrCreateType(self,fieldNode, register):
+        if register.valueType == "scalar":
             return self.getOrCreateScalarType(fieldNode,register)
         elif register.valueType == "array":
             return self.getOrCreateArrayType(fieldNode,register)
@@ -100,6 +121,12 @@ class FESADesignGenerator3_0_0(object):
             return self.getOrCreateStringArrayType(fieldNode,register)
         elif register.valueType == "stringArray2D":
             iecommon.logError('ERROR: In register '+register.name+' - 2D array of strings not supported in FESA.', True, {'errorlog': True})
+        elif register.valueType == "custom-type-scalar":
+            return self.getOrCreateCustomScalarType(fieldNode, register)
+        elif register.valueType == "custom-type-array":
+            return self.getOrCreateCustomArrayType(fieldNode, register)
+        elif register.valueType == "custom-type-array2D":
+            return self.getOrCreateCustom2DArrayType(fieldNode, register)
         else:
             iecommon.logError('ERROR: Unknown data-type:' + register.valueType, True, {'errorlog': True})
         return None
diff --git a/silecs-codegen/src/xml/fesa/fesa_3_0_0/generateSourceCode.py b/silecs-codegen/src/xml/fesa/fesa_3_0_0/generateSourceCode.py
index 17913a7..1652d43 100644
--- a/silecs-codegen/src/xml/fesa/fesa_3_0_0/generateSourceCode.py
+++ b/silecs-codegen/src/xml/fesa/fesa_3_0_0/generateSourceCode.py
@@ -122,6 +122,13 @@ def genCppSource(className, silecsRoot, fesaRoot, sourcePath,logTopics):
                     finalSource += fesaTemplates.genCGetArrayReg(register)
                 elif register.valueType == 'array2D':
                     finalSource += fesaTemplates.genCGetArray2DReg(register)
+                elif register.valueType == 'custom-type-scalar':
+                    finalSource += fesaTemplates.genCGetCustomScalarReg(register)
+                elif register.valueType == 'custom-type-array':
+                    finalSource += fesaTemplates.genCGetCustomArrayReg(register)
+                elif register.valueType == 'custom-type-array2D':
+                    finalSource += fesaTemplates.genCGetCustomArray2DReg(register)
+
             finalSource += '\n    }\n'
 
         if block.isWritable():
@@ -139,6 +146,12 @@ def genCppSource(className, silecsRoot, fesaRoot, sourcePath,logTopics):
                     finalSource += fesaTemplates.genCSetArrayReg(register)
                 elif register.valueType == 'array2D':
                     finalSource += fesaTemplates.genCSetArray2DReg(register)
+                elif register.valueType == 'custom-type-scalar':
+                    finalSource += fesaTemplates.genCSetScalarReg(register)
+                elif register.valueType == 'custom-type-array':
+                    finalSource += fesaTemplates.genCSetCustomArrayReg(register)
+                elif register.valueType == 'custom-type-array2D':
+                    finalSource += fesaTemplates.genCSetCustomArray2DReg(register)
             finalSource += fesaTemplates.cSend
             finalSource += '    }\n'
             finalSource += fesaTemplates.genCDatatypeSet(block.name,block.getFesaName(), className)
@@ -156,6 +169,12 @@ def genCppSource(className, silecsRoot, fesaRoot, sourcePath,logTopics):
                     finalSource += fesaTemplates.genCSetArrayRegData(register)
                 elif register.valueType == 'scalar':
                     finalSource += fesaTemplates.genCSetScalarRegData(register)
+                elif register.valueType == 'custom-type-scalar':
+                    finalSource += fesaTemplates.genCSetScalarRegData(register)
+                elif register.valueType == 'custom-type-array':
+                    finalSource += fesaTemplates.genCSetCustomArrayRegData(register)
+                elif register.valueType == 'custom-type-array2D':
+                    finalSource += fesaTemplates.genCSetCustomArray2DRegData(register)
 
             finalSource += fesaTemplates.cSend
             finalSource += '\n    }\n'   # closing bracket for block
diff --git a/silecs-codegen/src/xml/fesa/fesa_7_3_0/generateFesaDesign.py b/silecs-codegen/src/xml/fesa/fesa_7_3_0/generateFesaDesign.py
index 73dcf42..0640a47 100644
--- a/silecs-codegen/src/xml/fesa/fesa_7_3_0/generateFesaDesign.py
+++ b/silecs-codegen/src/xml/fesa/fesa_7_3_0/generateFesaDesign.py
@@ -96,3 +96,42 @@ class FESADesignGenerator7_3_0(fesa.fesa_3_1_0.generateFesaDesign.FESADesignGene
         else:
             globalConfigurationNode = globalDataNode.xpathEval("configuration")[0]
         self.getOrCreatePLCClassVersionField(globalConfigurationNode,designClass.version)
+
+        # Fill custom-types with enums.
+        enums = designClass.getEnums()
+        customTypesNode = doc.xpathEval('/equipment-model/custom-types')[0]
+        for enum in enums:
+            enumElement = getOrCreateNamedChildElement(customTypesNode, "enum", enum.name)
+            existing_items = enumElement.xpathEval("item")
+            to_add = []
+            to_edit = []
+
+            # Loop through all new enums and check which should be added and which edited.
+            for item in enum.items:
+                if item.symbol in [item.prop("symbol") for item in existing_items]:
+                    to_edit.append(item)
+                else:
+                    to_add.append(item)
+
+            # Loop though all existing enums and remove any which are not in new enums.
+            to_add_edit = to_add + to_edit
+            for existing in existing_items:
+                if existing.prop("symbol") not in [item.symbol for item in to_add_edit]:
+                    existing.unlinkNode()
+                    existing.freeNode()
+
+            # Edit existing items.
+            for item in to_edit:
+                for existing in existing_items:
+                    if existing.prop("symbol") != item.symbol:
+                        continue
+                    existing.setProp("access", item.access)
+                    existing.setProp("value", item.value)
+            
+            # Add missing items.
+            for item in to_add:
+                itemElement = libxml2.newNode("item")
+                enumElement.addChild(itemElement)
+                itemElement.setProp("access", item.access)
+                itemElement.setProp("value", item.value)
+                itemElement.setProp("symbol", item.symbol)
diff --git a/silecs-codegen/src/xml/genduwrapper.py b/silecs-codegen/src/xml/genduwrapper.py
index 2034155..7e466b2 100644
--- a/silecs-codegen/src/xml/genduwrapper.py
+++ b/silecs-codegen/src/xml/genduwrapper.py
@@ -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})
 
diff --git a/silecs-codegen/src/xml/genduwrappertemplate.py b/silecs-codegen/src/xml/genduwrappertemplate.py
index 8ee582d..083df06 100644
--- a/silecs-codegen/src/xml/genduwrappertemplate.py
+++ b/silecs-codegen/src/xml/genduwrappertemplate.py
@@ -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():
@@ -500,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():
diff --git a/silecs-codegen/src/xml/genplcsrc.py b/silecs-codegen/src/xml/genplcsrc.py
index d96b55a..c332c14 100644
--- a/silecs-codegen/src/xml/genplcsrc.py
+++ b/silecs-codegen/src/xml/genplcsrc.py
@@ -543,7 +543,7 @@ def generateBeckhoffSources(param, sourceFolderPath, 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
@@ -556,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()
diff --git a/silecs-codegen/src/xml/model/Class/Block.py b/silecs-codegen/src/xml/model/Class/Block.py
index 92ae5c4..3cb9ad3 100644
--- a/silecs-codegen/src/xml/model/Class/Block.py
+++ b/silecs-codegen/src/xml/model/Class/Block.py
@@ -35,8 +35,9 @@ class Block(object):
     ___acquisitionIOBlockName = "Acquisition-IO-Block"
     ___commandIOBlockName = "Command-IO-Block"
     
-    def __init__(self, xmlNode):
+    def __init__(self, xmlNode, customTypesXmlNode):
         self.xmlNode = xmlNode
+        self.customTypesXmlNode = customTypesXmlNode
         self.ioType = ""
         self.name = xmlNode.prop("name")
         if xmlNode.hasProp("ioType"):
@@ -109,8 +110,8 @@ class ParamBlock(Block):
         self.address = 0
         self.memSize = 0
             
-    def initWithParamBlockNode(self, xmlNode):
-        super(ParamBlock, self).__init__(xmlNode)
+    def initWithParamBlockNode(self, xmlNode, customTypesXmlNode):
+        super(ParamBlock, self).__init__(xmlNode, customTypesXmlNode)
         self.size = int(self.xmlNode.prop("size"))
         self.address = int(self.xmlNode.prop("address"))
         self.memSize = int(self.xmlNode.prop("mem-size"))
@@ -121,7 +122,7 @@ class ParamBlock(Block):
         if designBlock.xmlNode.hasProp("ioType"):
             newNode.newProp("ioType", designBlock.xmlNode.prop("ioType"))
         print(newNode.get_name())
-        super(ParamBlock, self).__init__(newNode)
+        super(ParamBlock, self).__init__(newNode, designBlock.customTypesXmlNode)
         newNode.newProp("size", str(self.size))
         newNode.newProp("address", str(self.address))
         newNode.newProp("mem-size", str(self.memSize))
@@ -142,7 +143,7 @@ class ParamBlock(Block):
         paramRegisters = []
         for registerNode in self.getRegisterNodes():
             paramRegister = ParamRegister()
-            paramRegister.initWithParamRegisterNode(registerNode)
+            paramRegister.initWithParamRegisterNode(registerNode, self.customTypesXmlNode)
             paramRegisters.append(paramRegister)
         return paramRegisters
     
@@ -157,8 +158,8 @@ class ParamBlock(Block):
                 
 class DesignBlock(Block):
             
-    def __init__(self, xmlNode):
-        super(DesignBlock, self).__init__(xmlNode)
+    def __init__(self, xmlNode, customTypesXmlNode):
+        super(DesignBlock, self).__init__(xmlNode, customTypesXmlNode)
         self.generateFesaProperty = False
         self.fesaPropertyName = ""
         self.fesaGetServerActionName = ""
@@ -188,13 +189,13 @@ class DesignBlock(Block):
     def getDesignMEMRegisters(self):
         designRegisters = []
         for registerNode in self.getMEMRegisterNodes():
-            designRegisters.append(DesignRegister(registerNode))
+            designRegisters.append(DesignRegister(registerNode, self.customTypesXmlNode))
         return designRegisters
         
     def getDesignIORegisters(self):
         designIORegisters = []
         for registerNode in self.getIORegisterNodes():
-            designIORegisters.append(DesignRegister(registerNode))
+            designIORegisters.append(DesignRegister(registerNode, self.customTypesXmlNode))
         return designIORegisters
     
     def getDesignRegisters(self):
diff --git a/silecs-codegen/src/xml/model/Class/Class.py b/silecs-codegen/src/xml/model/Class/Class.py
index b1cbfe1..ef91395 100644
--- a/silecs-codegen/src/xml/model/Class/Class.py
+++ b/silecs-codegen/src/xml/model/Class/Class.py
@@ -18,11 +18,18 @@ from iecommon import *
 from model.Class.Block import *
 from Mapping import *
 import libxml2
+from model.Class.Types import Enum
 
 class Class(object):
     
     def __init__(self, xmlNode):
         self.xmlNode = xmlNode
+        customTypes = self.xmlNode.xpathEval("custom-types")
+        if customTypes is not None and len(customTypes) == 1:
+            self.customTypesXmlNode = customTypes[0]
+        else:
+            self.customTypesXmlNode = None
+
         self.name = xmlNode.prop("name")
         self.version = xmlNode.prop("version")
         #xmlNode.shellPrintNode()
@@ -39,6 +46,9 @@ class Class(object):
     def getMemoryBlockNodes(self):
         return self.xmlNode.xpathEval("*[name()='Acquisition-Block' or name()='Setting-Block' or name()='Command-Block' or name()='Configuration-Block']")
     
+    def getEnumNodes(self):
+        return self.xmlNode.xpathEval("custom-types/enum")
+
 class ParamClass(Class):
     ___addressAttribute = { MEM_TYPE: "address", DI_TYPE: "DI-address", DO_TYPE: "DO-address", AI_TYPE: "AI-address", AO_TYPE: "AO-address" }
     ___usedDataAttribute = { MEM_TYPE: "used-mem", DI_TYPE: "used-DI", DO_TYPE: "used-DO", AI_TYPE: "used-AI", AO_TYPE: "used-AO" }
@@ -58,6 +68,9 @@ class ParamClass(Class):
         newNode = libxml2.newNode(designClass.xmlNode.get_name())
         newNode.newProp("name", designClass.name)
         newNode.newProp("version", designClass.version)
+        if designClass.customTypesXmlNode is not None:
+            customNode = designClass.customTypesXmlNode.copyNode(1)
+            newNode.addChild(customNode)
         super(ParamClass, self).__init__(newNode)
         for index, address in enumerate(self.address):
             newNode.newProp(self.___addressAttribute[index], str(self.address[index]))
@@ -76,7 +89,7 @@ class ParamClass(Class):
         paramBlocks = []
         for blockNode in self.getBlockNodes():
             paramBlock = ParamBlock()
-            paramBlock.initWithParamBlockNode(blockNode)
+            paramBlock.initWithParamBlockNode(blockNode, self.customTypesXmlNode)
             paramBlocks.append(paramBlock)
         return paramBlocks
     
@@ -84,7 +97,7 @@ class ParamClass(Class):
         paramMemBlocks = []
         for blockNode in self.getMemoryBlockNodes():
             paramBlock = ParamBlock()
-            paramBlock.initWithParamBlockNode(blockNode)
+            paramBlock.initWithParamBlockNode(blockNode, self.customTypesXmlNode)
             paramMemBlocks.append(paramBlock)
         return paramMemBlocks
     
@@ -111,15 +124,21 @@ class DesignClass(Class):
     def getDesignBlocks(self):
         designBlocks = []
         for blockNode in self.getBlockNodes():
-            designBlocks.append(DesignBlock(blockNode))
+            designBlocks.append(DesignBlock(blockNode, self.customTypesXmlNode))
         return designBlocks
     
     def getDesignMemoryBlocks(self):
         designBlocks = []
         for blockNode in self.getMemoryBlockNodes():
-            designBlocks.append(DesignBlock(blockNode))
+            designBlocks.append(DesignBlock(blockNode, self.customTypesXmlNode))
         return designBlocks
     
+    def getEnums(self):
+        enums = []
+        for node in self.getEnumNodes():
+            enums.append(Enum(node))
+        return enums
+
     @staticmethod
     def getDesignClassFromRootNode(silecsRoot):
         classNodes = silecsRoot.xpathEval('/SILECS-Design/SILECS-Class')
diff --git a/silecs-codegen/src/xml/model/Class/Register.py b/silecs-codegen/src/xml/model/Class/Register.py
index c665f59..1d21520 100644
--- a/silecs-codegen/src/xml/model/Class/Register.py
+++ b/silecs-codegen/src/xml/model/Class/Register.py
@@ -25,9 +25,9 @@ class Register(object):
     ___acquisitionIORegisterType = "Acquisition-IO-Register"
     
     
-    def __init__(self, xmlNode):
+    def __init__(self, xmlNode, customTypesXmlNode):
         self.xmlNode = xmlNode
-        
+        self.customTypesXmlNode = customTypesXmlNode
         #xmlNode.shellPrintNode()
         self.name = xmlNode.prop("name")
         self.___type = xmlNode.get_name()
@@ -37,8 +37,9 @@ class Register(object):
         self.dim2 = 1
         self.stringLength = 1 # ... currently needs to be default because of some old convention
         self.format = ""
+        self.custom_type_name = ""
 
-        valueTypes = xmlNode.xpathEval("*[name()='scalar' or name()='array' or name()='array2D' or name()='string' or name()='stringArray' or name()='stringArray2D']")
+        valueTypes = xmlNode.xpathEval("*[name()='scalar' or name()='array' or name()='array2D' or name()='string' or name()='stringArray' or name()='stringArray2D' or name()='custom-type-scalar' or name()='custom-type-array' or name()='custom-type-array2D']")
         if not valueTypes:
             iecommon.logError('ERROR: The register '+ self.name +' has no valueTypes.', True, {'errorlog': True})
         if len(valueTypes) < 1:
@@ -59,7 +60,15 @@ class Register(object):
         if self.valueTypeNode.hasProp("string-length"):
             self.stringLength = int(self.valueTypeNode.prop("string-length"))
 
-        self.format = self.valueTypeNode.prop("format")
+        if self.isCustomType():
+            self.custom_type_name = self.valueTypeNode.prop("custom-type-name-ref")
+
+            if self.isEnumType():
+                self.format = "int32"
+            else:
+                raise Exception("Custom types other than 'enum' are not yet supported.")
+        else:
+            self.format = self.valueTypeNode.prop("format")
             
 
     def getNameCapitalized(self):
@@ -89,6 +98,29 @@ class Register(object):
     def isStringType(self):
         return self.valueType == 'string' or self.valueType == 'stringArray' or self.valueType == 'stringArray2D'
     
+    def isCustomScalar(self):
+        return self.valueType == 'custom-type-scalar'
+
+    def isCustomArray(self):
+        return self.valueType == 'custom-type-array'
+
+    def isCustomArray2D(self):
+        return self.valueType == 'custom-type-array2D'
+
+    def isCustomType(self):
+        return self.isCustomScalar() or self.isCustomArray() or self.isCustomArray2D()
+
+    def isEnumType(self):
+        if self.customTypesXmlNode is None:
+            return False
+
+        enums = self.customTypesXmlNode.xpathEval("enum")
+        for e in enums:
+            if e.prop("name") == self.custom_type_name:
+                return True
+
+        return False
+
     def isArrayType(self):
         return self.isArray() or self.isArray2D()
     
@@ -203,8 +235,8 @@ class ParamRegister(Register):
         self.memSize = 0
         self.bitNumber = 0
         
-    def initWithParamRegisterNode(self, xmlNode):
-        super(ParamRegister, self).__init__(xmlNode)
+    def initWithParamRegisterNode(self, xmlNode, customTypesXmlNode):
+        super(ParamRegister, self).__init__(xmlNode, customTypesXmlNode)
         self.size = int(self.xmlNode.prop("size"))
         self.address = int(self.xmlNode.prop("address"))
         self.memSize = int(self.xmlNode.prop("mem-size"))
@@ -218,7 +250,7 @@ class ParamRegister(Register):
         newNodeTree = designRegister.xmlNode.copyNode(1) # 1 is for recursive copy
         if ( newNodeTree == None ):
             iecommon.logError('ERROR: Failed to copy register node: '+ designRegister.name +'.', True, {'errorlog': True})
-        super(ParamRegister, self).__init__(designRegister.xmlNode)
+        super(ParamRegister, self).__init__(designRegister.xmlNode, designRegister.customTypesXmlNode)
         self.xmlNode = newNodeTree
         self.size = self.getDataSize()
         newNodeTree.newProp("size", str(self.size))
@@ -231,7 +263,11 @@ class ParamRegister(Register):
             newNodeTree.newProp("bit-number", str(bitNumber))
             self.bitNumber = bitNumber
 
-        #self.xmlNode.shellPrintNode()
+        # Add an attribute to facilitate detecting the type of the custom-type.
+        if self.isEnumType():
+            for elem in self.xmlNode:
+                if elem.hasProp("custom-type-name-ref"):
+                    elem.newProp("type", "enum")
         
     def setSize(self,size):
         self.xmlNode.setProp("size", str(size))
@@ -248,8 +284,8 @@ class ParamRegister(Register):
         
 class DesignRegister(Register):
     
-    def __init__(self, xmlNode):
-        super(DesignRegister, self).__init__(xmlNode)
+    def __init__(self, xmlNode, customTypesXmlNode):
+        super(DesignRegister, self).__init__(xmlNode, customTypesXmlNode)
         self.fesaFieldName = self.name
         self.generateFesaValueItem=True
         if self.xmlNode.hasProp("fesaFieldName"):
diff --git a/silecs-codegen/src/xml/model/Class/Types.py b/silecs-codegen/src/xml/model/Class/Types.py
new file mode 100644
index 0000000..2d3f324
--- /dev/null
+++ b/silecs-codegen/src/xml/model/Class/Types.py
@@ -0,0 +1,25 @@
+class EnumItem(object):
+    def __init__(self, xmlNode) -> None:
+        self.xmlNode = xmlNode
+        self.access = xmlNode.prop("access")
+        self.symbol = xmlNode.prop("symbol")
+        self.value = xmlNode.prop("value")
+
+    def __str__(self) -> str:
+        return f"EnumItem: {self.access}, {self.symbol}, {self.value}"
+
+class Enum(object):
+    def __init__(self, xmlNode) -> None:
+        self.xmlNode = xmlNode
+
+        self.name: str = xmlNode.prop("name")
+        self.items: EnumItem = []
+        items = xmlNode.xpathEval("item")
+        for item in items:
+            self.items.append(EnumItem(item))
+    
+    def __str__(self) -> str:
+        items = ""
+        for item in self.items:
+            items += "\t"+ str(item) + "\n"
+        return f"Enum {self.name}.\nItems:\n {items}"
\ No newline at end of file
diff --git a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsRegister.cpp b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsRegister.cpp
index 1cb6a07..3d99d9d 100644
--- a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsRegister.cpp
+++ b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsRegister.cpp
@@ -37,8 +37,30 @@ Register::Register(const ElementXML& registerNode) :
     for (auto childIter = childNodes.begin(); childIter != childNodes.end(); childIter++)
     {
         // We have to loop, since there is as well the description-element
-        type_ = whichFormatType(childIter->getAttribute("format"));
-        typeString_ = childIter->getAttribute("format");
+        
+        if (childIter->hasAttribute("custom-type-name-ref"))
+        {
+            if (!childIter->hasAttribute("type"))
+            {
+                throw SilecsException(__FILE__, __LINE__, "Register '" + name_+ "' - Can't detect the type of the custom-type.");
+            }
+
+            if (childIter->getAttribute("type") == "enum")
+            {
+                // Enum type.
+                type_ = Int32;
+                typeString_ = "int32";
+            }
+            else
+            {
+                throw SilecsException(__FILE__, __LINE__, "Register '" + name_+ "' - Custom types other than 'enum' are not yet supported.");
+            }
+        }
+        else
+        {
+            type_ = whichFormatType(childIter->getAttribute("format"));
+            typeString_ = childIter->getAttribute("format");
+        }
         StringUtilities::toLower(typeString_);
 
         if (childIter->hasAttribute("dim"))
diff --git a/silecs-model/src/xml/DeploySchema.xsd b/silecs-model/src/xml/DeploySchema.xsd
index 8577a26..cdb7309 100644
--- a/silecs-model/src/xml/DeploySchema.xsd
+++ b/silecs-model/src/xml/DeploySchema.xsd
@@ -589,13 +589,6 @@
         </xs:restriction>
     </xs:simpleType>
 
-    <!-- Base type which just forbid characters not supported by the DB -->
-    <xs:simpleType name="BaseType">
-        <xs:restriction base="xs:string">
-            <xs:pattern value="[^&amp;&lt;&gt;&quot;]*" />
-        </xs:restriction>
-    </xs:simpleType>
-    
     <!-- Type for named item which are not transformed into Cpp object -->
     <xs:simpleType name="NonIdentifierType">
         <xs:restriction base="BaseType">
diff --git a/silecs-model/src/xml/DesignSchema-custom-types.xsd b/silecs-model/src/xml/DesignSchema-custom-types.xsd
new file mode 100644
index 0000000..4fe83ec
--- /dev/null
+++ b/silecs-model/src/xml/DesignSchema-custom-types.xsd
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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/. -->
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
+    <xs:include schemaLocation="DesignSchema-data-types.xsd" />
+    <xs:include schemaLocation="shared.xsd" />
+
+    <xs:simpleType name="MeaningValueType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="ON" />
+            <xs:enumeration value="OFF" />
+            <xs:enumeration value="WARNING" />
+            <xs:enumeration value="ERROR" />
+            <xs:enumeration value="NONE" />
+        </xs:restriction>
+    </xs:simpleType>
+    
+    <xs:element name="enum">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="description" type="DescriptionType" minOccurs="0" maxOccurs="unbounded" />
+                <xs:element name="item" maxOccurs="unbounded">
+                    <xs:complexType>
+                        <xs:attribute name="symbol" type="EnumSymbolNameType" use="required" />
+                        <xs:attribute name="value" type="xs:int" use="required" />
+                        <xs:attribute name="meaning" type="MeaningValueType" default="NONE" />
+                        <xs:attribute name="access" type="EnumAccessType" use="required" />
+                    </xs:complexType>
+                </xs:element>
+            </xs:sequence>
+            <xs:attribute name="name" type="TypeNameType" use="required" />
+        </xs:complexType>
+        
+        <xs:unique name="item-symbol-is-unique-per-enum">
+            
+            <xs:selector xpath="./item" />
+            
+            <xs:field xpath="@symbol" />
+        </xs:unique>
+        
+        <xs:unique name="item-value-is-unique-per-enum">
+            
+            <xs:selector xpath="./item" />
+            
+            <xs:field xpath="@value" />
+        </xs:unique>
+    </xs:element>
+    
+    <!-- This type can be extended in the lab-package -->
+    <xs:complexType name="custom-type-collection">
+        <xs:sequence>
+            <!-- Stacked sequences to have variable order for the sub-elements -->
+            <xs:sequence minOccurs="0" maxOccurs="unbounded">
+                <xs:element ref="enum" minOccurs="0" maxOccurs="unbounded" />
+            </xs:sequence>
+        </xs:sequence>
+    </xs:complexType>
+
+</xs:schema>
\ No newline at end of file
diff --git a/silecs-model/src/xml/DesignSchema-data-types.xsd b/silecs-model/src/xml/DesignSchema-data-types.xsd
new file mode 100644
index 0000000..67f8d37
--- /dev/null
+++ b/silecs-model/src/xml/DesignSchema-data-types.xsd
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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/. -->
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
+
+
+    <xs:simpleType name="IdentifierType">
+        <xs:restriction base="xs:string">
+            <xs:pattern value="[A-Za-z][A-Za-z0-9_]*" />
+        </xs:restriction>
+    </xs:simpleType>
+    
+    <xs:simpleType name="TypeNameType">
+        <xs:restriction base="IdentifierType">
+            <xs:maxLength value="60" />
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="EnumAccessType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="RW" />
+            <xs:enumeration value="RO" />
+            <xs:enumeration value="WO" />
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="EnumSymbolNameType">
+        <xs:restriction base="xs:string">
+            <xs:pattern value="[_A-Za-z][_A-Za-z0-9]*" />
+            <xs:maxLength value="60" />
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:complexType name="TypeRefType">
+        <xs:attribute name="data-type-name-ref" type="xs:string" use="required" />
+    </xs:complexType>
+    
+    <xs:simpleType name="FormatType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="uint8" />
+            <xs:enumeration value="int8" />
+            <xs:enumeration value="uint16" />
+            <xs:enumeration value="int16" />
+            <xs:enumeration value="uint32" />
+            <xs:enumeration value="int32" />
+            <xs:enumeration value="uint64" />
+            <xs:enumeration value="int64" />
+            <xs:enumeration value="float32" />
+            <xs:enumeration value="float64" />
+            <xs:enumeration value="date" />
+            <xs:enumeration value="char" />
+            <xs:enumeration value="byte" />
+            <xs:enumeration value="word" />
+            <xs:enumeration value="dword" />
+            <xs:enumeration value="int" />
+            <xs:enumeration value="dint" />
+            <xs:enumeration value="real" />
+            <xs:enumeration value="dt" />
+            <xs:enumeration value="bool" />
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="IOFormatType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="uint8" />
+            <xs:enumeration value="uint16" />
+            <xs:enumeration value="uint32" />
+            <xs:enumeration value="byte" />
+            <xs:enumeration value="word" />
+            <xs:enumeration value="dword" />
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="DimensionType">
+        <xs:restriction base="xs:unsignedInt">
+            <xs:minInclusive value="1" />
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="LengthType">
+        <xs:restriction base="xs:unsignedInt">
+            <xs:minInclusive value="2" />
+            <xs:maxInclusive value="254" />
+        </xs:restriction>
+    </xs:simpleType>
+    
+    <xs:complexType name="ScalarValueType">
+        <xs:attribute name="format" type="FormatType" use="required" />
+    </xs:complexType>
+
+    <xs:complexType name="IO-ScalarValueType">
+        <xs:attribute name="format" type="IOFormatType" use="required" />
+    </xs:complexType>
+
+    <xs:complexType name="ArrayValueType">
+        <xs:attribute name="format" type="FormatType" use="required" />
+        <xs:attribute name="dim" type="DimensionType" use="required" />
+    </xs:complexType>
+
+    <xs:complexType name="Array2DValueType">
+        <xs:attribute name="format" type="FormatType" use="required" />
+        <xs:attribute name="dim1" type="DimensionType" use="required" />
+        <xs:attribute name="dim2" type="DimensionType" use="required" />
+    </xs:complexType>
+
+    <xs:complexType name="StringValueType">
+        <xs:attribute name="format" fixed="string" use="required" />
+        <xs:attribute name="string-length" type="LengthType" use="required" />
+    </xs:complexType>
+
+    <xs:complexType name="StringArrayValueType">
+        <xs:attribute name="format" fixed="string" use="required" />
+        <xs:attribute name="string-length" type="LengthType" use="required" />
+        <xs:attribute name="dim" type="DimensionType" use="required" />
+    </xs:complexType>
+
+    <xs:complexType name="StringArray2DValueType">
+        <xs:attribute name="format" fixed="string" use="required" />
+        <xs:attribute name="string-length" type="LengthType" use="required" />
+        <xs:attribute name="dim1" type="DimensionType" use="required" />
+        <xs:attribute name="dim2" type="DimensionType" use="required" />
+    </xs:complexType>
+
+    <xs:complexType name="CustomScalarValueType">
+        <xs:attribute name="custom-type-name-ref" type="xs:string" use="required" />
+    </xs:complexType>
+    
+    <xs:complexType name="CustomArrayValueType">
+        <xs:attribute name="custom-type-name-ref" type="xs:string" use="required" />
+        <xs:attribute name="dim" type="DimensionType" use="required" />
+    </xs:complexType>
+
+    <xs:complexType name="CustomArray2DValueType">
+        <xs:attribute name="custom-type-name-ref" type="xs:string" use="required" />
+        <xs:attribute name="dim1" type="DimensionType" use="required" />
+        <xs:attribute name="dim2" type="DimensionType" use="required" />
+    </xs:complexType>
+</xs:schema>
\ No newline at end of file
diff --git a/silecs-model/src/xml/DesignSchema.xsd b/silecs-model/src/xml/DesignSchema.xsd
index 52ff0ca..eeec6e0 100644
--- a/silecs-model/src/xml/DesignSchema.xsd
+++ b/silecs-model/src/xml/DesignSchema.xsd
@@ -3,7 +3,9 @@
 
 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
     <xs:include schemaLocation="shared.xsd" />
-
+    <xs:include schemaLocation="DesignSchema-data-types.xsd" />
+    <xs:include schemaLocation="DesignSchema-custom-types.xsd" />
+    
     <xs:element name="SILECS-Design">
         <xs:annotation>
             <xs:documentation>A SILECS configuration contains the class designs, the memory mapping and the hardware distribution for a given set of PLCs.</xs:documentation>
@@ -89,6 +91,7 @@
                                 <xs:element name="Command-IO-Block" type="Setting-IO-BlockType" />
                             </xs:choice>
                             <xs:element name="Description" type="xs:string" minOccurs="0" />
+                            <xs:element name="custom-types" type="custom-type-collection" minOccurs="0" />
                         </xs:sequence>
                         <xs:attribute name="name" type="SilecsClassNameType" use="required" />
                         <xs:attribute name="version" type="VersionType" use="required" />
@@ -104,6 +107,14 @@
                             </xs:simpleType>
                         </xs:attribute>
                     </xs:complexType>
+                    <xs:key name="custom-key">
+                        <xs:selector xpath="./custom-types/*" />
+                        <xs:field xpath="@name" />
+                    </xs:key>
+                    <xs:keyref name="custom-scalar-keyref" refer="custom-key">
+                        <xs:selector xpath=".//custom-type-scalar | .//custom-type-array | .//custom-type-array2D"/>
+                        <xs:field xpath="@custom-type-name-ref"/>
+                    </xs:keyref>
                     <xs:unique name="Register-name-has-to-be-unique">
                         <xs:selector xpath="*/*" />
                         <xs:field xpath="@name" />
@@ -315,43 +326,6 @@
         </xs:complexContent>
     </xs:complexType>
 
-    <xs:complexType name="ScalarValueType">
-        <xs:attribute name="format" type="FormatType" use="required" />
-    </xs:complexType>
-
-    <xs:complexType name="IO-ScalarValueType">
-        <xs:attribute name="format" type="IOFormatType" use="required" />
-    </xs:complexType>
-
-    <xs:complexType name="ArrayValueType">
-        <xs:attribute name="format" type="FormatType" use="required" />
-        <xs:attribute name="dim" type="DimensionType" use="required" />
-    </xs:complexType>
-
-    <xs:complexType name="Array2DValueType">
-        <xs:attribute name="format" type="FormatType" use="required" />
-        <xs:attribute name="dim1" type="DimensionType" use="required" />
-        <xs:attribute name="dim2" type="DimensionType" use="required" />
-    </xs:complexType>
-
-    <xs:complexType name="StringValueType">
-        <xs:attribute name="format" fixed="string" use="required" />
-        <xs:attribute name="string-length" type="LengthType" use="required" />
-    </xs:complexType>
-
-    <xs:complexType name="StringArrayValueType">
-        <xs:attribute name="format" fixed="string" use="required" />
-        <xs:attribute name="string-length" type="LengthType" use="required" />
-        <xs:attribute name="dim" type="DimensionType" use="required" />
-    </xs:complexType>
-
-    <xs:complexType name="StringArray2DValueType">
-        <xs:attribute name="format" fixed="string" use="required" />
-        <xs:attribute name="string-length" type="LengthType" use="required" />
-        <xs:attribute name="dim1" type="DimensionType" use="required" />
-        <xs:attribute name="dim2" type="DimensionType" use="required" />
-    </xs:complexType>
-
     <xs:complexType name="BaseRegisterType">
         <xs:sequence>
             <xs:element name="Description" type="xs:string" minOccurs="0" />
@@ -388,6 +362,9 @@
                         <xs:element name="string" type="StringValueType" />
                         <xs:element name="stringArray" type="StringArrayValueType" />
                         <xs:element name="stringArray2D" type="StringArray2DValueType" />
+                        <xs:element name="custom-type-scalar" type="CustomScalarValueType" />
+                        <xs:element name="custom-type-array" type="CustomArrayValueType" />
+                        <xs:element name="custom-type-array2D" type="CustomArray2DValueType" />
                     </xs:choice>
                 </xs:sequence>
             </xs:extension>
@@ -399,6 +376,7 @@
             <xs:extension base="BaseRegisterType">
                 <xs:sequence>
                     <xs:element name="scalar" type="IO-ScalarValueType" />
+                    <xs:element name="custom-type-scalar" type="CustomScalarValueType" />
                 </xs:sequence>
             </xs:extension>
         </xs:complexContent>
@@ -421,52 +399,5 @@
             <xs:pattern value="[_A-Za-z]+[_A-Za-z0-9]*" />
         </xs:restriction>
     </xs:simpleType>
-    <xs:simpleType name="FormatType">
-        <xs:restriction base="xs:string">
-            <xs:enumeration value="uint8" />
-            <xs:enumeration value="int8" />
-            <xs:enumeration value="uint16" />
-            <xs:enumeration value="int16" />
-            <xs:enumeration value="uint32" />
-            <xs:enumeration value="int32" />
-            <xs:enumeration value="uint64" />
-            <xs:enumeration value="int64" />
-            <xs:enumeration value="float32" />
-            <xs:enumeration value="float64" />
-            <xs:enumeration value="date" />
-            <xs:enumeration value="char" />
-            <xs:enumeration value="byte" />
-            <xs:enumeration value="word" />
-            <xs:enumeration value="dword" />
-            <xs:enumeration value="int" />
-            <xs:enumeration value="dint" />
-            <xs:enumeration value="real" />
-            <xs:enumeration value="dt" />
-            <xs:enumeration value="bool" />
-        </xs:restriction>
-    </xs:simpleType>
-
-    <xs:simpleType name="IOFormatType">
-        <xs:restriction base="xs:string">
-            <xs:enumeration value="uint8" />
-            <xs:enumeration value="uint16" />
-            <xs:enumeration value="uint32" />
-            <xs:enumeration value="byte" />
-            <xs:enumeration value="word" />
-            <xs:enumeration value="dword" />
-        </xs:restriction>
-    </xs:simpleType>
-
-    <xs:simpleType name="DimensionType">
-        <xs:restriction base="xs:unsignedInt">
-            <xs:minInclusive value="1" />
-        </xs:restriction>
-    </xs:simpleType>
-    <xs:simpleType name="LengthType">
-        <xs:restriction base="xs:unsignedInt">
-            <xs:minInclusive value="2" />
-            <xs:maxInclusive value="254" />
-        </xs:restriction>
-    </xs:simpleType>
 
 </xs:schema>
diff --git a/silecs-model/src/xml/shared.xsd b/silecs-model/src/xml/shared.xsd
index ee68b9c..22b9a1a 100644
--- a/silecs-model/src/xml/shared.xsd
+++ b/silecs-model/src/xml/shared.xsd
@@ -30,4 +30,26 @@ along with this program.  If not, see http://www.gnu.org/licenses/.-->
         </xs:restriction>
     </xs:simpleType>
     
+        <!-- Base type which just forbid characters not supported by the DB -->
+    <xs:simpleType name="BaseType">
+        <xs:restriction base="xs:string">
+            <xs:pattern value="[^&amp;&lt;&gt;&quot;]*" />
+        </xs:restriction>
+    </xs:simpleType>
+
+    <!-- We must allow empty content for defining empty strings in default values -->
+    <xs:simpleType name="ValueType">
+        <xs:restriction base="BaseType">
+            <xs:whiteSpace value="collapse" />
+            <xs:maxLength value="4000" />
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="DescriptionType">
+        <xs:restriction base="BaseType">
+            <xs:whiteSpace value="collapse" />
+            <xs:maxLength value="4000" />
+        </xs:restriction>
+    </xs:simpleType>
+    
     </xs:schema>
-- 
GitLab