diff --git a/code/iec61131-integration/.module-id b/code/iec61131-integration/.module-id
new file mode 100644
index 000000000..021ebf015
--- /dev/null
+++ b/code/iec61131-integration/.module-id
@@ -0,0 +1 @@
+00c8d7a5-e1fd-4a2c-bbdf-9d0d4ac80394
diff --git a/code/iec61131-integration/build.gradle.kts b/code/iec61131-integration/build.gradle.kts
new file mode 100644
index 000000000..5f49f5196
--- /dev/null
+++ b/code/iec61131-integration/build.gradle.kts
@@ -0,0 +1,39 @@
+import org.fbme.gradle.moduleDependency
+
+plugins {
+ kotlin
+ mps
+}
+
+dependencies {
+ compileOnly(mpsDistribution())
+ compileOnly(project(":code:library"))
+ compileOnly(project(":code:language"))
+ compileOnly(project(":code:platform"))
+ implementation(project(":code:iec61131"))
+
+ mpsImplementation(project(":code:library", "mps"))
+ mpsImplementation(project(":code:language", "mps"))
+ mpsImplementation(project(":code:platform", "mps"))
+ mpsImplementation(project(":code:richediting", "mps"))
+}
+
+mps {
+ buildScriptName.set("fbme_iec61131")
+ moduleName.set("org.fbme.integration.iec61131.lib")
+ moduleDependency(project(":code:library"))
+ moduleDependency(project(":code:platform"))
+}
+
+val mpsPrepare by tasks.getting(Copy::class) {
+ from("build/libs/iec61131-integration.jar")
+ into("solutions/org.fbme.integration.iec61131/lib")
+}
+
+val test by tasks.getting(Test::class) {
+ dependsOn(
+ ":code:library:buildDistPlugin",
+ ":code:platform:buildDistPlugin",
+ "buildDistPlugin"
+ )
+}
\ No newline at end of file
diff --git a/code/iec61131-integration/src/main/kotlin/org/fbme/integration/iec61131/importer/Iec61131ImportLocationConfig.kt b/code/iec61131-integration/src/main/kotlin/org/fbme/integration/iec61131/importer/Iec61131ImportLocationConfig.kt
new file mode 100644
index 000000000..672bd8f92
--- /dev/null
+++ b/code/iec61131-integration/src/main/kotlin/org/fbme/integration/iec61131/importer/Iec61131ImportLocationConfig.kt
@@ -0,0 +1,33 @@
+package org.fbme.integration.iec61131.importer
+
+import com.intellij.ide.util.BrowseFilesListener
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
+import com.intellij.openapi.fileChooser.FileChooserFactory
+import com.intellij.openapi.util.EmptyRunnable
+import com.intellij.ui.FieldPanel
+import com.intellij.ui.InsertPathAction
+import com.intellij.util.ui.JBUI
+import org.fbme.ide.platform.projectWizard.Iec61499SolutionSettings
+import javax.swing.JLabel
+import javax.swing.JTextField
+
+class Iec61131ImportLocationConfig(defaultName: String) :
+ Iec61499SolutionSettings(defaultName) {
+ private val importLocation: JTextField
+
+ init {
+ this.add(JLabel("IEC61131 file:"), 4, 0.0)
+ importLocation = JTextField()
+ importLocation.name = "Path"
+ val descriptor = FileChooserDescriptorFactory.createSingleFileDescriptor()
+ InsertPathAction.addTo(importLocation, descriptor)
+ val listener = BrowseFilesListener(importLocation, "Choose IEC61131 project xml file", "", descriptor)
+ val fieldPanel = FieldPanel(importLocation, null, null, listener, EmptyRunnable.getInstance())
+ FileChooserFactory.getInstance().installFileCompletion(fieldPanel.textField, descriptor, false, null)
+ this.add(fieldPanel, 5, 0.0, JBUI.insetsBottom(5))
+ }
+
+ fun getImportLocation(): String {
+ return importLocation.text.trim { it <= ' ' }
+ }
+}
\ No newline at end of file
diff --git a/code/iec61131-integration/src/main/kotlin/org/fbme/integration/iec61131/importer/Iec61131IntegrationIcons.kt b/code/iec61131-integration/src/main/kotlin/org/fbme/integration/iec61131/importer/Iec61131IntegrationIcons.kt
new file mode 100644
index 000000000..d9b22d1cc
--- /dev/null
+++ b/code/iec61131-integration/src/main/kotlin/org/fbme/integration/iec61131/importer/Iec61131IntegrationIcons.kt
@@ -0,0 +1,9 @@
+package org.fbme.integration.iec61131.importer
+
+import com.intellij.openapi.util.IconLoader
+
+object Iec61131IntegrationIcons {
+
+ @JvmField
+ val importProject = IconLoader.getIcon("/icons/iec61131_import.svg", Iec61131IntegrationIcons::class.java)
+}
\ No newline at end of file
diff --git a/code/iec61131-integration/src/main/kotlin/org/fbme/integration/iec61131/importer/Iec61131ProjectTemplate.kt b/code/iec61131-integration/src/main/kotlin/org/fbme/integration/iec61131/importer/Iec61131ProjectTemplate.kt
new file mode 100644
index 000000000..9a99fdafa
--- /dev/null
+++ b/code/iec61131-integration/src/main/kotlin/org/fbme/integration/iec61131/importer/Iec61131ProjectTemplate.kt
@@ -0,0 +1,51 @@
+package org.fbme.integration.iec61131.importer
+
+import jetbrains.mps.project.AbstractModule
+import jetbrains.mps.smodel.ModelImports
+import org.fbme.ide.iec61499.repository.PlatformElement
+import org.fbme.ide.iec61499.repository.PlatformElementsOwner
+import org.fbme.ide.iec61499.repository.PlatformRepository
+import org.fbme.ide.platform.projectWizard.Iec61499ProjectTemplate
+import org.fbme.lib.iec61131.converter.ProjectConverter
+import org.jetbrains.mps.openapi.model.SModel
+import org.jetbrains.mps.openapi.persistence.PersistenceFacade
+
+
+class Iec61131ImportProjectTemplate : Iec61499ProjectTemplate(
+ Iec61131ImportLocationConfig("NewModel"),
+ "IEC61131 Project",
+ "Convert IEC61131 project to IEC61499 FBME project",
+ Iec61131IntegrationIcons.importProject,
+ null
+) {
+
+ override fun initModel(repository: PlatformRepository, model: SModel): PlatformElement {
+ val settings = settings as Iec61131ImportLocationConfig
+ val iec61131ProjectLocation = settings.getImportLocation()
+ addModelImports(model)
+
+ readModel(iec61131ProjectLocation, model)
+ val first = model.rootNodes.first()
+
+ return repository.getAdapter(first, PlatformElement::class.java)
+ }
+
+ private fun addModelImports(model: SModel) {
+ val iec61499ModuleId = "b8a7e14f-52ea-4ee2-b17e-26c27da8084c(IEC-61499)"
+ (model.module as AbstractModule).addDependency(
+ PersistenceFacade.getInstance().createModuleReference(iec61499ModuleId), false
+ )
+
+ val stdLibModuleId = "r:fa98296a-e4fa-4f84-b917-968f5f770c4b(iec61499.4diac.stdlib)"
+ ModelImports(model).addModelImport(PersistenceFacade.getInstance().createModelReference(stdLibModuleId))
+ }
+
+
+ private fun readModel(path: String, model: SModel) {
+ val owner = PlatformElementsOwner();
+
+ ProjectConverter(owner.iec61499Factory, owner.stFactory).getProjectNodes(path)
+ .map { (it as PlatformElement).node }
+ .forEach { model.addRootNode(it) }
+ }
+}
diff --git a/code/iec61131-integration/src/main/resources/META-INF/plugin.xml b/code/iec61131-integration/src/main/resources/META-INF/plugin.xml
new file mode 100644
index 000000000..08ec2e1de
--- /dev/null
+++ b/code/iec61131-integration/src/main/resources/META-INF/plugin.xml
@@ -0,0 +1,22 @@
+
+
+ fbme.integration.iec61131
+ FBME - IEC61131
+ FBME projects creation from IEC61131 projects
+ 0.1
+ FBME
+
+ fbme.platform
+ jetbrains.mps.core
+ jetbrains.mps.execution.languages
+ fbme.richediting
+ fbme.scenes
+
+
+
+
+
+
+
+
+
diff --git a/code/iec61131-integration/src/main/resources/icons/iec61131_import.svg b/code/iec61131-integration/src/main/resources/icons/iec61131_import.svg
new file mode 100644
index 000000000..0a8c49ce3
--- /dev/null
+++ b/code/iec61131-integration/src/main/resources/icons/iec61131_import.svg
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/code/iec61131-integration/src/test/resources/ParserTestFbt1.fbt b/code/iec61131-integration/src/test/resources/ParserTestFbt1.fbt
new file mode 100644
index 000000000..6be53a225
--- /dev/null
+++ b/code/iec61131-integration/src/test/resources/ParserTestFbt1.fbt
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/code/iec61131/build.gradle.kts b/code/iec61131/build.gradle.kts
new file mode 100644
index 000000000..7ac5516f1
--- /dev/null
+++ b/code/iec61131/build.gradle.kts
@@ -0,0 +1,10 @@
+plugins {
+ kotlin
+ kotlin("plugin.serialization").version("1.6.21")
+}
+
+dependencies {
+ implementation("io.github.pdvrieze.xmlutil:serialization-jvm:0.83.0")
+ implementation("io.github.pdvrieze.xmlutil:core-jvm:0.83.0")
+ compileOnly(project(":code:library"))
+}
diff --git a/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/ConverterBase.kt b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/ConverterBase.kt
new file mode 100644
index 000000000..d4a72358b
--- /dev/null
+++ b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/ConverterBase.kt
@@ -0,0 +1,59 @@
+package org.fbme.lib.iec61131.converter
+
+import org.fbme.lib.iec61131.model.BlockInfo
+import org.fbme.lib.iec61131.model.DataParameterInfo
+import org.fbme.lib.iec61131.model.Iec61131Xml
+import org.fbme.lib.iec61131.model.oldStandardBocks
+import org.fbme.lib.iec61499.IEC61499Factory
+import org.fbme.lib.st.STFactory
+import org.fbme.lib.st.types.DataType
+import org.fbme.lib.st.types.ElementaryType
+import org.fbme.lib.st.types.GenericType
+
+open class ConverterBase(arguments: ConverterArguments) {
+ val factory: IEC61499Factory
+ val stFactory: STFactory
+ val blocksInterfaceInfo: BlocksInterfaceInfo
+
+ init {
+ factory = arguments.factory
+ stFactory = arguments.stFactory
+ blocksInterfaceInfo = arguments.blocksInterfaceInfo
+ }
+}
+
+class ConverterArguments(
+ val factory: IEC61499Factory,
+ val stFactory: STFactory,
+ val blocksInterfaceInfo: BlocksInterfaceInfo
+)
+
+class BlocksInterfaceInfo(pous: Iec61131Xml.Pous) {
+
+ private val typeNameToBlock: Map =
+ (oldStandardBocks + getAdditionalBlockTypes(pous)).associateBy { it.typeName }
+ fun getBlockParameters(blockTypeName: String): List {
+ return typeNameToBlock[blockTypeName]!!.parameterNameToType.values.toList()
+ }
+
+ private fun getAdditionalBlockTypes(pous: Iec61131Xml.Pous): List {
+ return pous.pouList.map { toBlockType(it) }
+ }
+
+ private val nameToType: Map = GenericType.values().associateBy { it.name } +
+ ElementaryType.values().associateBy { it.name }
+
+ private fun toBlockType(pou: Iec61131Xml.Pou): BlockInfo {
+ val parameters = pou.pouInterface?.outputVars?.toParameters() ?: emptyList()
+ return BlockInfo(pou.name, parameters)
+ }
+
+ private fun List.toParameters(): List {
+ return this.map { variableList ->
+ variableList.variableList.map { variable ->
+ val typeStr = variable.type.typeName
+ DataParameterInfo(variable.name, nameToType[typeStr]!!)
+ }
+ }.flatten()
+ }
+}
diff --git a/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbInterfaceConverter.kt b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbInterfaceConverter.kt
new file mode 100644
index 000000000..7aadd0263
--- /dev/null
+++ b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbInterfaceConverter.kt
@@ -0,0 +1,56 @@
+package org.fbme.lib.iec61131.converter
+
+import org.fbme.lib.iec61131.model.Iec61131Xml
+import org.fbme.lib.iec61499.declarations.EventAssociation
+import org.fbme.lib.iec61499.declarations.FBTypeDeclaration
+import org.fbme.lib.iec61499.declarations.ParameterDeclaration
+import org.fbme.lib.iec61499.parser.STConverter
+
+class FbInterfaceConverter(
+ private val xmlPou: Iec61131Xml.Pou,
+ converterArguments: ConverterArguments
+) : ConverterBase(converterArguments) {
+
+ fun fillInterface(fbtd: FBTypeDeclaration) {
+ fbtd.name = xmlPou.name
+
+ val inputEvent = factory.createEventDeclaration(null)
+ inputEvent.name = "REQ"
+
+ val outputEvent = factory.createEventDeclaration(null)
+ outputEvent.name = "CNF"
+
+ val pouInterface = xmlPou.pouInterface
+ if (pouInterface != null) {
+ fbtd.inputParameters.addAll(mapVarListToParameters(pouInterface.inputVars))
+ fbtd.inputParameters.addAll(mapVarListToParameters(pouInterface.inOutVars))
+
+ fbtd.outputParameters.addAll(mapVarListToParameters(pouInterface.outputVars))
+ fbtd.outputParameters.addAll(mapVarListToParameters(pouInterface.inOutVars))
+
+ fbtd.inputParameters.forEach { inputEvent.associations.add(createAssociation(it.name)) }
+ fbtd.outputParameters.forEach { outputEvent.associations.add(createAssociation(it.name)) }
+ }
+ fbtd.inputEvents.add(inputEvent)
+ fbtd.outputEvents.add(outputEvent)
+ }
+
+ private fun createAssociation(varName: String): EventAssociation {
+ val association = factory.createEventAssociation()
+ association.parameterReference.setTargetName(varName)
+ return association
+ }
+
+ private fun mapVarListToParameters(varListList: List): List {
+ return varListList
+ .map { it.variableList.map(::mapVariableToParameter) }
+ .flatten()
+ }
+
+ private fun mapVariableToParameter(xmlVariable: Iec61131Xml.VariableList.Variable): ParameterDeclaration {
+ val parameterDeclaration = factory.createParameterDeclaration(null)
+ parameterDeclaration.name = xmlVariable.name
+ parameterDeclaration.type = STConverter.parseType(stFactory, xmlVariable.type.typeName)
+ return parameterDeclaration
+ }
+}
\ No newline at end of file
diff --git a/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbNetworkConverter.kt b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbNetworkConverter.kt
new file mode 100644
index 000000000..a7ac493ca
--- /dev/null
+++ b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbNetworkConverter.kt
@@ -0,0 +1,161 @@
+package org.fbme.lib.iec61131.converter
+
+import org.fbme.lib.iec61131.service.InterfaceService
+import org.fbme.lib.iec61499.fbnetwork.*
+import kotlin.random.Random
+
+class FbNetworkConverter(
+ fbdInfo: FbdInfo,
+ converterArguments: ConverterArguments,
+ private val startEvent: String = "REQ",
+ private val endEvent: String? = "CNF"
+) : ConverterBase(converterArguments) {
+
+ private val networkEventConverter =
+ FbTranslator(fbdInfo, converterArguments, startEvent, endEvent)
+ private val interfaceService = InterfaceService(fbdInfo.xmlInterface)
+
+
+ // returns additional FBTypeDeclarations of variables
+ fun fillNetwork(network: FBNetwork) {
+
+ // blocks with assignments
+ val assignments = networkEventConverter
+ .networkConnections
+ .filterIsInstance()
+ .groupBy { it.blockName }
+ val blocks = networkEventConverter.networkConnections.filterIsInstance(NetworkPart.Block::class.java)
+ .mapIndexed { pos, blockDto ->
+ val blockAssigns = assignments.getOrDefault(blockDto.name, ArrayList())
+ createFunctionBlock(blockDto, pos, blockAssigns)
+ }
+ network.functionBlocks.addAll(blocks)
+
+ val connections = networkEventConverter
+ .networkConnections
+ .filterIsInstance()
+
+ // endpoints
+ val endpointCoordinates = getEndpointCoordinates(blocks.size)
+ network.endpointCoordinates.addAll(endpointCoordinates.values)
+
+ // event connections
+ val eventConnections = connections.filter { it.type == EntryKind.EVENT }.map { convertConnection(it) }
+ network.eventConnections.addAll(eventConnections)
+
+ // data connections
+ val blockNameToPosition = networkEventConverter.networkConnections
+ .filterIsInstance(NetworkPart.Block::class.java)
+ .mapIndexed { index, block -> Pair(block.name, index) }
+ .associate { it }
+ val dataConnectionDtos = connections.filter { it.type == EntryKind.DATA }
+ val dataConnections = createDataConnections(dataConnectionDtos, blockNameToPosition)
+ network.dataConnections.addAll(dataConnections)
+ }
+
+ private fun createDataConnections(
+ dataConnectionDtos: List,
+ blockNameToPosition: Map,
+ ): List {
+ class FreeCords(var left: Int, var right: Int)
+
+ // -1: in variables
+ // 0: block
+ // ...
+ // blocks.size: out variables
+ val posToCords = mutableMapOf()
+ for (it in -1..blockNameToPosition.size) {
+ posToCords[it] = FreeCords(20, 20)
+ }
+
+ val connections = ArrayList()
+
+ for (connectionDto in dataConnectionDtos) {
+ val sourceSplit = connectionDto.source.split(".")
+ val sourcePos: Int = if (sourceSplit.size == 1) {
+ -1 // in variable
+ } else {
+ blockNameToPosition[sourceSplit[0]]!!
+ }
+
+ val targetSplit = connectionDto.target.split(".")
+ val targetPos: Int = if (targetSplit.size == 1) {
+ blockNameToPosition.size // out variable
+ } else {
+ blockNameToPosition[targetSplit[0]]!!
+ }
+ val connection = factory.createFBNetworkConnection(EntryKind.DATA)
+ connection.sourceReference.setFQName(connectionDto.source)
+ connection.targetReference.setFQName(connectionDto.target)
+
+ // if the source before the target - default straight link
+ // else - long 4-corner link created below
+ if (sourcePos + 1 != targetPos) {
+ val sourceCords = posToCords[sourcePos]!!
+ val targetCords = posToCords[targetPos]!!
+ connection.path = ConnectionPath(sourceCords.right, Random.nextInt(300, 1000), targetCords.left)
+
+ sourceCords.right += 20
+ targetCords.left += 20
+ }
+
+ connections.add(connection)
+ }
+ return connections
+ }
+
+ private fun createFunctionBlock(
+ blockDto: NetworkPart.Block,
+ pos: Int,
+ assignments: List
+ ): FunctionBlockDeclaration {
+ val block = factory.createFunctionBlockDeclaration(null)
+ block.name = blockDto.name
+ block.typeReference.setTargetName(blockDto.type)
+ block.x = 500 * (pos + 2)
+ block.y = 0
+
+ val parameterAssignments = assignments.map {
+ val parameterAssign = factory.createParameterAssignment()
+ parameterAssign.value = it.literal
+ parameterAssign.parameterReference.setTargetName(it.inputVarName)
+ parameterAssign
+ }
+ block.parameters.addAll(parameterAssignments)
+ return block
+ }
+
+ private fun getEndpointCoordinates(blocksNumber: Int): Map {
+ val endpointCoordinates = mutableMapOf()
+ val inputVariables = interfaceService.getInputVariables() + interfaceService.getInOutVariables()
+ val outputVariables = interfaceService.getOutputVariables() + interfaceService.getInOutVariables()
+ for (i in inputVariables.indices) {
+ val varName = inputVariables[i]
+ endpointCoordinates[varName] = createEndpointCoordinate(varName, 0, 100 * (i + 1))
+ }
+ for (i in outputVariables.indices) {
+ val varName = outputVariables[i]
+ endpointCoordinates[varName] = createEndpointCoordinate(varName, 500 + (blocksNumber + 3) * 500, 100 * (i + 1))
+ }
+ if (startEvent == "REQ")
+ endpointCoordinates["REQ"] = createEndpointCoordinate("REQ", 0, 0)
+ if (endEvent == "CNF")
+ endpointCoordinates["CNF"] = createEndpointCoordinate("CNF", 500 + (blocksNumber + 3) * 500, 0)
+ return endpointCoordinates
+ }
+
+ private fun createEndpointCoordinate(name: String, x: Int, y: Int): EndpointCoordinate {
+ val endpointCoordinate = factory.createEndpointCoordinate()
+ endpointCoordinate.portReference.setFQName(name)
+ endpointCoordinate.x = x
+ endpointCoordinate.y = y
+ return endpointCoordinate
+ }
+
+ private fun convertConnection(connection: NetworkPart.Connection): FBNetworkConnection {
+ val convConnection = factory.createFBNetworkConnection(connection.type)
+ convConnection.sourceReference.setFQName(connection.source)
+ convConnection.targetReference.setFQName(connection.target)
+ return convConnection
+ }
+}
\ No newline at end of file
diff --git a/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbTranslator.kt b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbTranslator.kt
new file mode 100644
index 000000000..cc11bc3c0
--- /dev/null
+++ b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbTranslator.kt
@@ -0,0 +1,222 @@
+package org.fbme.lib.iec61131.converter
+
+import org.fbme.lib.iec61131.service.*
+import org.fbme.lib.iec61499.fbnetwork.EntryKind
+import org.fbme.lib.iec61499.parser.STConverter
+import org.fbme.lib.st.types.ElementaryType
+import org.fbme.lib.st.types.GenericType
+
+class FbTranslator(
+ val fbdInfo: FbdInfo,
+ converterArguments: ConverterArguments,
+ private var curEventOut: String,
+ private val endEventIn: String?
+) : ConverterBase(converterArguments) {
+
+ private val blockService = FbdBlockService(fbdInfo.xmlFbd)
+ private val varService = FbdVariableService(fbdInfo.xmlFbd, fbdInfo.xmlInterface, converterArguments)
+ private val evaluationOrderService = FbdEvaluationOrderService(fbdInfo.xmlFbd, fbdInfo.xmlInterface, converterArguments)
+ private val inConnectionsService = BlockInConnectionsService(fbdInfo.xmlFbd)
+ private val interfaceService = InterfaceService(fbdInfo.xmlInterface)
+ val networkConnections: List
+ private val outputVariables = interfaceService.getInOutVariables() + interfaceService.getOutputVariables()
+
+ private val varNameToConnection = mutableMapOf()
+ private val outVarToConnection = mutableMapOf()
+ private val connectionToType = HashMap(varService.allVarTypes)
+ private val variableIdToOutConnections = mutableMapOf>()
+ private val varNameToInputsWithInitValue = mutableMapOf>()
+ private var holderCnt = 0
+
+ init {
+ interfaceService.getInputVariables().forEach { varNameToConnection[it] = it }
+ interfaceService.getInOutVariables().forEach { varNameToConnection[it] = it }
+ networkConnections = getConnections()
+ }
+
+ /**
+ * create connections between blocks in topological order and their in/out/inout variables
+ */
+ private fun getConnections(): List {
+ val connections = ArrayList()
+ for (to in evaluationOrderService.evaluationOrder) {
+ val newConnections = when (to) {
+ is FbdEvaluationOrderService.Block -> connectBlock(to.id)
+ is FbdEvaluationOrderService.OutVar -> connectOutVar(to)
+ }
+ connections.addAll(newConnections)
+ }
+ outVarToConnection.forEach { connections.add(createConnection(it.value, it.key, EntryKind.DATA)) }
+
+ for (varName in varNameToInputsWithInitValue.keys) {
+ // if varName hasn't got assignments
+ if (varName !in varNameToConnection) continue
+
+ connections.addAll(createHolderWithConnections(varName, "_var"))
+ for (initValueConnection in varNameToInputsWithInitValue[varName]!!) {
+ connections.add(createConnection(varNameToConnection[varName]!!, initValueConnection, EntryKind.DATA))
+ }
+ }
+
+ if (endEventIn != null) {
+ connections.add(createConnection(curEventOut, endEventIn, EntryKind.EVENT))
+ }
+ return connections
+ }
+
+ private fun createHolderWithConnections(
+ varName: String,
+ holderNameSuffix: String
+ ): List {
+ val connections = ArrayList()
+ // holder block after the main part of network containing variable
+ val holderName = varName + holderNameSuffix
+ holderCnt++
+
+ connections.add(NetworkPart.Block(holderName, "F_MOVE"))
+ connections.add(NetworkPart.Assignment(holderName, "IN", varService.getInitValue(varName)))
+ connections.add(createConnection(curEventOut, "$holderName.REQ", EntryKind.EVENT))
+ curEventOut = "$holderName.CNF"
+
+ val connectionWithVar = varNameToConnection[varName]
+ if (connectionWithVar != null) {
+ connections.add(createConnection(connectionWithVar, "$holderName.IN", EntryKind.DATA))
+ } else {
+ varNameToInputsWithInitValue.putIfAbsent(varName, ArrayList())
+ varNameToInputsWithInitValue[varName]!!.add("$holderName.IN")
+ }
+ varNameToConnection[varName] = "$holderName.OUT"
+ return connections
+ }
+
+ private fun connectOutVar(toVar: FbdEvaluationOrderService.OutVar): Collection {
+ if (toVar.connection == null) {
+ return ArrayList()
+ }
+ val connections = ArrayList()
+ val refId = toVar.connection.refLocalId
+ val connectionName = if (blockService.isBlockId(refId)) {
+ blockService.getNameById(refId) + "." + toVar.connection.formalParameter
+ } else if (varService.isVariableId(refId)) {
+ val varName = varService.getNameById(refId)
+ if (varName !in varNameToConnection.keys) {
+ connections.addAll(createHolderWithConnections(varName, "_init"))
+ }
+ varNameToConnection[varName]!!
+ } else {
+ throw Iec61131ConverterException(fbdInfo.name, "not yet implemented")
+ }
+ varNameToConnection[toVar.name] = connectionName
+ if (toVar.name in outputVariables) {
+ outVarToConnection[toVar.name] = connectionName
+ }
+ return connections + getVarCallbacks(toVar)
+ }
+
+ private fun getVarCallbacks(outVar: FbdEvaluationOrderService.OutVar): List {
+ val connections = variableIdToOutConnections[outVar.id] ?: return emptyList()
+ val callbacks = ArrayList()
+ for (connection in connections) {
+ callbacks.add(createConnection(varNameToConnection[outVar.name]!!, connection, EntryKind.DATA))
+ }
+ return callbacks
+ }
+
+ private fun connectBlock(blockId: Long): List {
+ val blockConnections = ArrayList()
+ val blockName = blockService.getNameById(blockId)
+ val blockType = blockService.getTypeById(blockId)
+ blockConnections.add(NetworkPart.Block(blockName, blockType))
+ for (connection in inConnectionsService.getBlockInConnections(blockId)) {
+ blockConnections.addAll(processBlockDataConnection(connection, blockName))
+ }
+ assignTypeToBlockOutParameters(blockId)
+ blockConnections.add(createConnection(curEventOut, "$blockName.REQ", EntryKind.EVENT))
+ curEventOut = "$blockName.CNF"
+ return blockConnections
+ }
+
+ private fun assignTypeToBlockOutParameters(blockId: Long) {
+ val blockName = blockService.getNameById(blockId)
+ val blockType = blockService.getTypeById(blockId)
+ val typeMapper = HashMap()
+ for (parameter in blocksInterfaceInfo.getBlockParameters(blockType)) {
+ val connection = blockName + "." + parameter.name
+ if (parameter.type is GenericType && connectionToType[connection] != null) {
+ val varType = connectionToType[connection]!!
+ typeMapper[parameter.type] = when(varType) {
+ is ElementaryType -> varType
+ else -> throw Iec61131ConverterException(fbdInfo.name, "Var connected to generic input must be elementary type")
+ }
+ }
+ }
+ for (parameter in blocksInterfaceInfo.getBlockParameters(blockType)) {
+ val connection = blockName + "." + parameter.name
+ if (connectionToType[connection] == null) {
+ connectionToType[connection] = when (parameter.type) {
+ is ElementaryType -> parameter.type
+ is GenericType -> typeMapper[parameter.type]!!
+ else -> throw Iec61131ConverterException(fbdInfo.name, "Composite types are not supported")
+ }
+ }
+ }
+ }
+
+ private fun processBlockDataConnection(
+ connection: BlockInConnection,
+ toBlockName: String,
+ ): List {
+ if (blockService.isBlockId(connection.sourceId)) {
+ val to = toBlockName + "." + connection.targetBlockVariableName
+ val from = blockService.getNameById(connection.sourceId) + "." + connection.sourceFormalParameter
+ transferType(from, to)
+
+ return listOf(
+ createConnection(from, to, EntryKind.DATA),
+ createDefaultAssignment(toBlockName, connection.targetBlockVariableName)
+ )
+ } else if (varService.isVariableId(connection.sourceId)) {
+ return createVarToBlockConnections(connection, toBlockName)
+ }
+ return emptyList()
+ }
+
+ private fun createDefaultAssignment(blockName: String, inputVarName: String): NetworkPart.Assignment {
+ val to = "$blockName.$inputVarName"
+ val toDefaultValue = STConverter.parseLiteral(stFactory, getDefaultValue(connectionToType[to]!!))!!
+ return NetworkPart.Assignment(blockName, inputVarName, toDefaultValue)
+ }
+
+ private fun createVarToBlockConnections(
+ connection: BlockInConnection,
+ toBlockName: String
+ ): List {
+ val varName = varService.getNameById(connection.sourceId)
+ val varConnection = varNameToConnection[varName]
+ val to = toBlockName + "." + connection.targetBlockVariableName
+
+ variableIdToOutConnections.putIfAbsent(connection.sourceId, ArrayList())
+ variableIdToOutConnections[connection.sourceId]!!.add(to)
+ return if (varConnection != null) {
+ transferType(varConnection, to)
+ listOf(
+ createConnection(varConnection, to, EntryKind.DATA),
+ createDefaultAssignment(toBlockName, connection.targetBlockVariableName)
+ )
+ } else {
+ varNameToInputsWithInitValue.putIfAbsent(varName, ArrayList())
+ varNameToInputsWithInitValue[varName]!!.add(to)
+ transferType(varName, to)
+ val initValue = varService.getInitValue(varName)
+ listOf(NetworkPart.Assignment(toBlockName, connection.targetBlockVariableName, initValue))
+ }
+ }
+
+ private fun transferType(source: String, target: String) {
+ connectionToType[target] = connectionToType[source]
+ }
+
+ private fun createConnection(source: String, target: String, type: EntryKind): NetworkPart.Connection {
+ return NetworkPart.Connection(source, target, type)
+ }
+}
diff --git a/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbdInfo.kt b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbdInfo.kt
new file mode 100644
index 000000000..705b39b48
--- /dev/null
+++ b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbdInfo.kt
@@ -0,0 +1,9 @@
+package org.fbme.lib.iec61131.converter
+
+import org.fbme.lib.iec61131.model.Iec61131Xml
+
+class FbdInfo(
+ val name: String,
+ val xmlFbd: Iec61131Xml.FBD,
+ val xmlInterface: Iec61131Xml.Interface,
+)
\ No newline at end of file
diff --git a/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/Iec61131ConverterException.kt b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/Iec61131ConverterException.kt
new file mode 100644
index 000000000..495237220
--- /dev/null
+++ b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/Iec61131ConverterException.kt
@@ -0,0 +1,16 @@
+package org.fbme.lib.iec61131.converter
+
+import org.fbme.lib.iec61131.model.Iec61131Xml
+
+class Iec61131ConverterException(message: String, cause: Throwable?) : RuntimeException(message, cause) {
+
+ constructor(pouName: String, message: String, cause: Throwable?) : this(
+ "Exception while processing pou [$pouName]: $message", cause
+ )
+
+ constructor(xmlPou: Iec61131Xml.Pou, message: String, cause: Throwable?) : this(xmlPou.name, message, cause)
+
+ constructor(pouName: String, message: String) : this(pouName, message, null)
+
+ constructor(message: String): this(message, null)
+}
\ No newline at end of file
diff --git a/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/NetworkPart.kt b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/NetworkPart.kt
new file mode 100644
index 000000000..d58b31c99
--- /dev/null
+++ b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/NetworkPart.kt
@@ -0,0 +1,24 @@
+package org.fbme.lib.iec61131.converter
+
+import org.fbme.lib.iec61499.fbnetwork.EntryKind
+import org.fbme.lib.st.expressions.Literal
+
+sealed class NetworkPart {
+
+ class Connection(
+ val source: String,
+ val target: String,
+ val type: EntryKind
+ ) : NetworkPart()
+
+ class Assignment(
+ val blockName: String,
+ val inputVarName: String,
+ val literal: Literal<*>
+ ) : NetworkPart()
+
+ class Block(
+ val name: String,
+ val type: String
+ ) : NetworkPart()
+}
diff --git a/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/ProjectConverter.kt b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/ProjectConverter.kt
new file mode 100644
index 000000000..d0b2880bc
--- /dev/null
+++ b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/ProjectConverter.kt
@@ -0,0 +1,40 @@
+package org.fbme.lib.iec61131.converter
+
+import org.fbme.lib.common.Declaration
+import org.fbme.lib.iec61131.model.Iec61131Xml
+import org.fbme.lib.iec61499.IEC61499Factory
+import org.fbme.lib.iec61499.declarations.CompositeFBTypeDeclaration
+import org.fbme.lib.st.STFactory
+
+class ProjectConverter(
+ private val factory: IEC61499Factory,
+ private val stFactory: STFactory
+) {
+ fun getProjectNodes(path: String): List {
+ val project = Iec61131Xml.serializeProject(path)
+ val blockInterfaceService = BlocksInterfaceInfo(project.types.pous)
+ val converterArguments = ConverterArguments(factory, stFactory, blockInterfaceService)
+ val resDeclarations = getChildNodes(converterArguments, project)
+
+ return resDeclarations + SystemConverter(project, converterArguments).createSystems()
+ }
+
+ private fun getChildNodes(
+ converterArguments: ConverterArguments,
+ project: Iec61131Xml.Project
+ ): List {
+ return project.types.pous.pouList.map { convertToCompositeFBType(it, converterArguments) }
+ }
+
+ private fun convertToCompositeFBType(
+ xmlPou: Iec61131Xml.Pou,
+ converterArguments: ConverterArguments
+ ): CompositeFBTypeDeclaration {
+ val fbdInfo = getFbdInfo(xmlPou)
+ val compositeFbtd = converterArguments.factory.createCompositeFBTypeDeclaration(null)
+ FbNetworkConverter(fbdInfo, converterArguments, "REQ", "CNF")
+ .fillNetwork(compositeFbtd.network)
+ FbInterfaceConverter(xmlPou, converterArguments).fillInterface(compositeFbtd)
+ return compositeFbtd
+ }
+}
\ No newline at end of file
diff --git a/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/SystemConverter.kt b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/SystemConverter.kt
new file mode 100644
index 000000000..7d5c383fc
--- /dev/null
+++ b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/SystemConverter.kt
@@ -0,0 +1,128 @@
+package org.fbme.lib.iec61131.converter
+
+import org.fbme.lib.common.Declaration
+import org.fbme.lib.iec61131.model.Iec61131Xml
+import org.fbme.lib.iec61499.declarations.DeviceDeclaration
+import org.fbme.lib.iec61499.declarations.ResourceDeclaration
+import org.fbme.lib.iec61499.declarations.SystemDeclaration
+import org.fbme.lib.iec61499.fbnetwork.EntryKind
+import org.fbme.lib.iec61499.fbnetwork.FBNetwork
+import org.fbme.lib.iec61499.fbnetwork.FBNetworkConnection
+import org.fbme.lib.iec61499.parser.STConverter
+import org.fbme.lib.st.expressions.Literal
+
+class SystemConverter(
+ private val xmlProject: Iec61131Xml.Project,
+ private val converterArguments: ConverterArguments
+) : ConverterBase(converterArguments) {
+
+
+ fun createSystems(): List {
+ val nameToFbdInfo = xmlProject.types.pous.pouList
+ .map { getFbdInfo(it) }
+ .associateBy { it.name }
+
+ val systemInfos = mutableListOf()
+ xmlProject.instances.configurations.configurationList.forEach { configuration ->
+ configuration.resourceList.forEach { resource ->
+ resource.taskList.forEach { task ->
+ task.pouInstanceList.forEach { pouInstance ->
+
+ val interval = STConverter.parseLiteral(stFactory, task.interval)
+ ?: throw Iec61131ConverterException("task [${task.name}] has no time interval", null)
+
+ val fbdInfo = nameToFbdInfo[pouInstance.typeName] ?: throw Iec61131ConverterException(
+ "No fb named [${pouInstance.typeName}] given in instance [${pouInstance.name}]")
+
+ systemInfos.add(SystemInfo(pouInstance.name, interval, fbdInfo))
+ }
+ }
+ }
+ }
+
+ return systemInfos.map { createSystem(it) }
+ }
+
+ private fun createSystem(systemInfo: SystemInfo): SystemDeclaration {
+ val system = factory.createSystemDeclaration(null)
+ system.name = systemInfo.instanceName
+
+ val application = factory.createApplicationDeclaration(null)
+ application.name = "App"
+ system.applications.add(application)
+
+ system.devices.add(createDevice(systemInfo))
+
+ val segment = factory.createSegmentDeclaration(null)
+ segment.name = "Ethernet"
+ segment.typeReference.setTargetName("Ethernet")
+ system.segments.add(segment)
+
+ val link = factory.createLink()
+ link.resourceReference.setFQName("Testee.MAIN_RES")
+ link.segmentReference.setTargetName("Ethernet")
+ system.links.add(link)
+
+ return system
+ }
+
+ private fun createDevice(systemInfo: SystemInfo): DeviceDeclaration {
+ val device = factory.createDeviceDeclaration(null)
+ device.name = "Testee"
+
+ device.typeReference.setTargetName("FORTE_PC")
+ val mgrId = factory.createParameterAssignment()
+ mgrId.parameterReference.setTargetName("MGR_ID")
+ mgrId.value = STConverter.parseLiteral(stFactory, "'localhost:61499'")
+ device.parameters.add(mgrId)
+
+ device.resources.add(createResource(systemInfo))
+
+ return device
+ }
+
+ private fun createResource(systemInfo: SystemInfo): ResourceDeclaration {
+ val resource = factory.createResourceDeclaration(null)
+ resource.name = "MAIN_RES"
+ resource.typeReference.setTargetName("EMB_RES")
+ val resourceNetwork = resource.network
+ fillNetwork(systemInfo, resourceNetwork)
+ return resource
+ }
+
+ private fun fillNetwork(systemInfo: SystemInfo, network: FBNetwork) {
+
+ val cycleTimeout = factory.createParameterAssignment()
+ cycleTimeout.parameterReference.setTargetName("DT")
+ cycleTimeout.value = systemInfo.interval
+
+ val cycle = factory.createFunctionBlockDeclaration(null)
+ cycle.name = "E_CYCLE"
+ cycle.typeReference.setTargetName("E_CYCLE")
+ cycle.parameters.add(cycleTimeout)
+ cycle.x = 500
+ network.functionBlocks.add(cycle)
+
+ FbNetworkConverter(
+ systemInfo.fbdInfo,
+ converterArguments,
+ startEvent = "E_CYCLE.EO",
+ endEvent = null
+ ).fillNetwork(network)
+ network.eventConnections.add(createEvent("START.COLD", "E_CYCLE.START"))
+ network.eventConnections.add(createEvent("START.WARM", "E_CYCLE.START"))
+ }
+
+ private fun createEvent(from: String, to: String): FBNetworkConnection {
+ val connection = factory.createFBNetworkConnection(EntryKind.EVENT)
+ connection.sourceReference.setFQName(from)
+ connection.targetReference.setFQName(to)
+ return connection
+ }
+
+ class SystemInfo(
+ val instanceName: String,
+ val interval: Literal<*>,
+ val fbdInfo: FbdInfo
+ )
+}
\ No newline at end of file
diff --git a/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/Utils.kt b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/Utils.kt
new file mode 100644
index 000000000..bba858238
--- /dev/null
+++ b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/Utils.kt
@@ -0,0 +1,26 @@
+package org.fbme.lib.iec61131.converter
+
+import org.fbme.lib.iec61131.model.Iec61131Xml
+import org.fbme.lib.st.types.DataType
+import org.fbme.lib.st.types.ElementaryType
+
+fun getDefaultValue(type: DataType): String {
+ return when (type) {
+ ElementaryType.BOOL -> "FALSE"
+ ElementaryType.INT -> "0"
+ ElementaryType.STRING -> ""
+ ElementaryType.TIME -> "0ms"
+ else -> throw RuntimeException("Not implemented")
+ }
+}
+
+fun getFbdInfo(xmlPou: Iec61131Xml.Pou): FbdInfo {
+
+ val firstBody = xmlPou.bodyList.getOrNull(0)
+ ?: throw Iec61131ConverterException(xmlPou, "pou has no body", null)
+ val fbd = firstBody.fbd
+ ?: throw Iec61131ConverterException(xmlPou, "non fbd bodies are not supported yet", null)
+ val pouInterface = xmlPou.pouInterface
+ ?: throw Iec61131ConverterException(xmlPou, "pou has no interface", null)
+ return FbdInfo(xmlPou.name, fbd, pouInterface)
+}
diff --git a/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/model/BlocksInfo.kt b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/model/BlocksInfo.kt
new file mode 100644
index 000000000..bb7bf2515
--- /dev/null
+++ b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/model/BlocksInfo.kt
@@ -0,0 +1,309 @@
+package org.fbme.lib.iec61131.model
+
+import org.fbme.lib.st.types.DataType
+import org.fbme.lib.st.types.ElementaryType
+import org.fbme.lib.st.types.GenericType
+
+enum class DataParameterType {
+ INPUT, OUTPUT, UNDEFINED
+}
+
+class DataParameterInfo(val name: String, val type: DataType, val paramType: DataParameterType) {
+ constructor(name: String, type: DataType) : this(name, type, DataParameterType.UNDEFINED)
+}
+
+class BlockInfo(
+ val oldTypeName: String?,
+ val typeName: String,
+ params: List
+) {
+ val parameterNameToType: Map = params.associateBy { it.name }
+
+ constructor(typeName: String, params: List) : this(null, typeName, params)
+}
+
+private fun inVar(name: String, type: DataType) = DataParameterInfo(name, type, DataParameterType.INPUT)
+private fun outVar(name: String, type: DataType) = DataParameterInfo(name, type, DataParameterType.OUTPUT)
+
+
+val oldStandardBocks = run {
+ val standardFunctionBlocks = listOf(
+ BlockInfo(
+ "TP", "FB_TP",
+ listOf(
+ inVar("IN", ElementaryType.BOOL),
+ inVar("PT", ElementaryType.TIME),
+ outVar("Q", ElementaryType.BOOL),
+ outVar("ET", ElementaryType.TIME)
+ )
+ ),
+ BlockInfo(
+ "TON", "FB_TON",
+ listOf(
+ inVar("IN", ElementaryType.BOOL),
+ inVar("PT", ElementaryType.TIME),
+ outVar("Q", ElementaryType.BOOL),
+ outVar("ET", ElementaryType.TIME)
+ )
+ ),
+ BlockInfo(
+ "TOF", "FB_TOF",
+ listOf(
+ inVar("IN", ElementaryType.BOOL),
+ inVar("PT", ElementaryType.TIME),
+ outVar("Q", ElementaryType.BOOL),
+ outVar("ET", ElementaryType.TIME)
+ )
+ ),
+ BlockInfo(
+ "SR", "FB_SR",
+ listOf(
+ inVar("S1", ElementaryType.BOOL),
+ inVar("R", ElementaryType.BOOL),
+ outVar("Q1", ElementaryType.BOOL),
+ )
+ ),
+ BlockInfo(
+ "RS", "FB_RS",
+ listOf(
+ inVar("S", ElementaryType.BOOL),
+ inVar("R1", ElementaryType.BOOL),
+ outVar("Q1", ElementaryType.BOOL),
+ )
+ ),
+ BlockInfo(
+ "F_TRIG", "FB_F_TRIG",
+ listOf(
+ inVar("CLK", ElementaryType.BOOL),
+ outVar("Q", ElementaryType.BOOL),
+ )
+ ),
+ BlockInfo(
+ "R_TRIG", "FB_R_TRIG",
+ listOf(
+ inVar("CLK", ElementaryType.BOOL),
+ outVar("Q", ElementaryType.BOOL),
+ )
+ ),
+ )
+ val typeConversionBlocks: List =
+ ElementaryType.values().map { a ->
+ ElementaryType.values().map { b ->
+ val type = a.name + "_TO_" + b.name
+
+ BlockInfo(
+ type, "F_$type",
+ listOf(
+ inVar("IN", a),
+ outVar("OUT", b)
+ )
+ )
+ }
+ }.flatten()
+ val realInOutVars =
+ listOf(
+ inVar("IN", GenericType.ANY_REAL),
+ inVar("OUT", GenericType.ANY_REAL)
+ )
+ val numericalBlocks = listOf(
+ BlockInfo(
+ "ABS", "F_ABS",
+ listOf(
+ inVar("IN", GenericType.ANY_NUM),
+ inVar("OUT", GenericType.ANY_NUM)
+ )
+ ),
+ BlockInfo("SQRT", "F_SQRT", realInOutVars),
+ BlockInfo("LN", "F_LN", realInOutVars),
+ BlockInfo("LOG", "F_LOG", realInOutVars),
+ BlockInfo("EXP", "F_EXP", realInOutVars),
+ BlockInfo("SIN", "F_SIN", realInOutVars),
+ BlockInfo("COS", "F_COS", realInOutVars),
+ BlockInfo("TAN", "F_TAN", realInOutVars),
+ BlockInfo("ASIN", "F_ASIN", realInOutVars),
+ BlockInfo("ACOS", "F_ACOS", realInOutVars),
+ BlockInfo("ATAN", "F_ATAN", realInOutVars),
+ )
+ val arithmeticBlocks = listOf(
+ BlockInfo(
+ "ADD", "F_ADD",
+ listOf(
+ inVar("IN1", GenericType.ANY_NUM),
+ inVar("IN2", GenericType.ANY_NUM),
+ outVar("OUT", GenericType.ANY_NUM),
+ )
+ ),
+ BlockInfo(
+ "MUL", "F_MUL",
+ listOf(
+ inVar("IN1", GenericType.ANY_NUM),
+ inVar("IN2", GenericType.ANY_NUM),
+ outVar("OUT", GenericType.ANY_NUM),
+ )
+ ),
+ BlockInfo(
+ "SUB", "F_SUB",
+ listOf(
+ inVar("IN1", GenericType.ANY_NUM),
+ inVar("IN2", GenericType.ANY_NUM),
+ outVar("OUT", GenericType.ANY_NUM),
+ )
+ ),
+ BlockInfo(
+ "DIV", "F_DIV",
+ listOf(
+ inVar("IN1", GenericType.ANY_NUM),
+ inVar("IN2", GenericType.ANY_NUM),
+ outVar("OUT", GenericType.ANY_NUM),
+ )
+ ),
+ BlockInfo(
+ "MOD", "F_MOD",
+ listOf(
+ inVar("IN1", GenericType.ANY_INT),
+ inVar("IN2", GenericType.ANY_INT),
+ outVar("OUT", GenericType.ANY_INT),
+ )
+ ),
+ BlockInfo(
+ "EXPT", "F_EXPT",
+ listOf(
+ inVar("IN1", GenericType.ANY_REAL),
+ inVar("IN2", GenericType.ANY_NUM),
+ outVar("OUT", GenericType.ANY_REAL),
+ )
+ ),
+ BlockInfo(
+ "MOVE", "F_MOVE",
+ listOf(
+ inVar("IN", GenericType.ANY),
+ outVar("OUT", GenericType.ANY),
+ )
+ ),
+ )
+ val timeBlocks = emptyList() // TODO
+ val anyBitVars: List = listOf(
+ inVar("IN", GenericType.ANY_BIT),
+ inVar("N", GenericType.ANY_INT),
+ outVar("OUT", GenericType.ANY_BIT)
+ )
+ val bitShiftBlocks = listOf(
+ BlockInfo("SHL", "F_SHL", anyBitVars),
+ BlockInfo("SHR", "F_SHR", anyBitVars),
+ BlockInfo("ROR", "F_ROR", anyBitVars),
+ BlockInfo("ROL", "F_ROL", anyBitVars),
+ )
+ val binOpAnyBitVars: List = listOf(
+ inVar("IN1", GenericType.ANY_BIT),
+ inVar("IN2", GenericType.ANY_BIT),
+ outVar("OUT", GenericType.ANY_BIT)
+ )
+ val bitwiseBlocks = listOf(
+ BlockInfo("AND", "F_AND", binOpAnyBitVars),
+ BlockInfo("OR", "F_OR", binOpAnyBitVars),
+ BlockInfo("XOR", "F_XOR", binOpAnyBitVars),
+ BlockInfo(
+ "NOT", "F_NOT",
+ listOf(
+ inVar("IN", GenericType.ANY_BIT),
+ outVar("OUT", GenericType.ANY_BIT),
+ )
+ ),
+ )
+ val comparisonVars = listOf(
+ inVar("IN1", GenericType.ANY),
+ inVar("IN2", GenericType.ANY),
+ outVar("OUT", ElementaryType.BOOL)
+ )
+ val comparisonBlocks = listOf(
+ BlockInfo("GT", "F_GT", comparisonVars),
+ BlockInfo("GE", "F_GE", comparisonVars),
+ BlockInfo("EQ", "F_EQ", comparisonVars),
+ BlockInfo("LT", "F_LT", comparisonVars),
+ BlockInfo("LE", "F_LE", comparisonVars),
+ BlockInfo("NE", "F_NE", comparisonVars),
+ )
+ val stringBlocks = listOf(
+ BlockInfo(
+ "LEN", "F_LEN",
+ listOf(
+ inVar("IN", ElementaryType.STRING),
+ outVar("OUT", ElementaryType.INT)
+ )
+ ),
+ BlockInfo(
+ "LEFT", "F_LEFT",
+ listOf(
+ inVar("IN", ElementaryType.STRING),
+ inVar("L", GenericType.ANY_INT),
+ outVar("OUT", ElementaryType.STRING),
+ )
+ ),
+ BlockInfo(
+ "RIGHT", "F_RIGHT",
+ listOf(
+ inVar("IN", ElementaryType.STRING),
+ inVar("L", GenericType.ANY_INT),
+ outVar("OUT", ElementaryType.STRING),
+ )
+ ),
+ BlockInfo(
+ "MID", "F_MID",
+ listOf(
+ inVar("IN", ElementaryType.STRING),
+ inVar("L", GenericType.ANY_INT),
+ inVar("P", GenericType.ANY_INT),
+ outVar("OUT", ElementaryType.STRING),
+ )
+ ),
+ BlockInfo(
+ "CONCAT", "F_CONCAT",
+ listOf(
+ inVar("IN1", ElementaryType.STRING),
+ inVar("IN2", GenericType.ANY_STRING),
+ outVar("OUT", ElementaryType.STRING),
+ )
+ ),
+ BlockInfo(
+ "INSERT", "F_INSERT",
+ listOf(
+ inVar("IN1", ElementaryType.STRING),
+ inVar("IN2", GenericType.ANY_STRING),
+ inVar("P", GenericType.ANY_INT),
+ outVar("OUT", ElementaryType.STRING),
+ )
+ ),
+ BlockInfo(
+ "DELETE", "F_DELETE",
+ listOf(
+ inVar("IN", ElementaryType.STRING),
+ inVar("L", GenericType.ANY_INT),
+ inVar("P", GenericType.ANY_INT),
+ outVar("OUT", ElementaryType.STRING),
+ )
+ ),
+ BlockInfo(
+ "FIND", "F_FIND",
+ listOf(
+ inVar("IN1", ElementaryType.STRING),
+ inVar("IN2", ElementaryType.STRING),
+ outVar("OUT", ElementaryType.INT),
+ )
+ ),
+ )
+
+ standardFunctionBlocks +
+ typeConversionBlocks +
+ numericalBlocks +
+ arithmeticBlocks +
+ timeBlocks +
+ bitShiftBlocks +
+ bitwiseBlocks +
+ comparisonBlocks +
+ stringBlocks
+}
+
+val oldTypeNameToBlockInfo = oldStandardBocks.associateBy { it.oldTypeName }
+fun convertBlockType(blockType: String): String {
+ return oldTypeNameToBlockInfo[blockType]?.typeName ?: blockType
+}
diff --git a/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/model/Iec61131Xml.kt b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/model/Iec61131Xml.kt
new file mode 100644
index 000000000..fe80a310b
--- /dev/null
+++ b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/model/Iec61131Xml.kt
@@ -0,0 +1,396 @@
+package org.fbme.lib.iec61131.model
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.buildClassSerialDescriptor
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+import nl.adaptivity.xmlutil.EventType
+import nl.adaptivity.xmlutil.serialization.XML
+import nl.adaptivity.xmlutil.serialization.XmlValue
+import kotlin.io.path.Path
+import kotlin.io.path.readText
+
+class Iec61131Xml {
+
+ companion object {
+ fun serializeProject(path: String): Project {
+ return XML {
+ policy = Iec61131XmlPolicy
+ }.decodeFromString(Path(path).readText())
+ }
+ }
+
+ @Serializable
+ class NotImplemented
+
+ @SerialName("project")
+ @Serializable
+ class Project(
+ val schemaVersion: String?,
+ val fileHeader: NotImplemented,
+ val contentHeader: NotImplemented,
+ val types: Types,
+ val instances: Instances,
+ val addData: AddData?,
+ val documentation: Documentation?
+ )
+
+ @Serializable
+ class Instances(
+ val configurations: Configurations
+ )
+
+ @Serializable
+ class Configurations(
+ val configurationList: List
+ )
+
+ @Serializable
+ class Configuration(
+ val resourceList: List
+ // ...
+ )
+
+ @Serializable
+ class Resource(
+ val taskList: List
+ // ...
+ )
+
+ @Serializable
+ class Task(
+ val pouInstanceList: List,
+ val interval: String?,
+ val name: String
+ // ...
+ )
+
+ @Serializable
+ class PouInstance(
+ val name: String,
+ val typeName: String
+ )
+
+ @Serializable
+ class AddData // ...
+
+ @Serializable
+ class Documentation // ...
+
+ @Serializable
+ class Types(
+ val dataTypes: NotImplemented?,
+ val pous: Pous,
+ )
+
+
+ @Serializable
+ class Pous(
+ val pouList: List
+ )
+
+ @Serializable
+ class Pou(
+ @SerialName("interface")
+ val pouInterface: Interface?,
+ val actions: Actions?,
+ val transitions: NotImplemented?,
+
+ val bodyList: List,
+
+ val name: String,
+ val pouType: String,
+ val globalId: String?,
+ )
+
+ @Serializable
+ class Actions // ...
+
+ @Serializable
+ class Body(
+ val il: NotImplemented?,
+
+ val st: NotImplemented?,
+
+ @SerialName("FBD")
+ val fbd: FBD?,
+
+ val ld: NotImplemented?,
+
+ val sfc: NotImplemented?,
+
+ val addData: AddData?,
+ val documentation: Documentation?,
+
+ val worksheetName: String?,
+
+ val globalId: String?
+
+ )
+
+ @Serializable
+ class Interface(
+ val returnType: DataType?,
+ val localVars: List,
+ val tempVars: List,
+ val inputVars: List,
+ val outputVars: List,
+ val inOutVars: List,
+ val externalVars: List,
+ val globalVars: List,
+ val accessVars: List,
+ val addData: AddData?,
+ val documentation: Documentation?
+ )
+
+ @Serializable(with = DataType.Companion::class)
+ class DataType(
+ val typeName: String
+ ) {
+ @Suppress("EXPERIMENTAL_API_USAGE")
+ @Serializer(DataType::class)
+ companion object : KSerializer {
+
+ override val descriptor = buildClassSerialDescriptor("DataType")
+ override fun serialize(encoder: Encoder, value: DataType) = throw RuntimeException("Not needed")
+
+ // example:
+ override fun deserialize(decoder: Decoder): DataType {
+ val reader = (decoder as XML.XmlInput).input
+
+ fun parseUntil(eventType: EventType) {
+ while (reader.eventType != eventType) reader.next()
+ }
+
+ parseUntil(EventType.START_ELEMENT)
+ // from example:
+ reader.next()
+ parseUntil(EventType.START_ELEMENT)
+ // from example:
+ val tagName = reader.localName
+ reader.next()
+ parseUntil(EventType.END_ELEMENT)
+ // from example: still
+ reader.next()
+ parseUntil(EventType.END_ELEMENT)
+ // from example:
+
+ return DataType(tagName)
+ }
+ }
+ }
+
+
+ @Serializable
+ class VariableList(
+ val name: String?,
+ val constant: Boolean?,
+ val retain: Boolean?,
+ val nonretain: Boolean?,
+ val persistent: Boolean?,
+ val nonpersistent: Boolean?,
+ val variableList: List,
+ val addData: AddData?,
+ val documentation: Documentation?
+ ) {
+ @Serializable
+ class Variable(
+ val type: DataType,
+ val initialValue: InitialValue?,
+ val addData: AddData?,
+ val documentation: Documentation?,
+ val name: String,
+ val address: String?,
+ val globalId: String?
+ )
+
+ @Serializable
+ class InitialValue(
+ val simpleValue: SimpleValue?
+ )
+
+ @Serializable
+ class SimpleValue(
+ val value: String
+ )
+ }
+
+
+ @Serializable
+ class Comment // ...
+
+ @Serializable
+ class Error // ...
+
+ @Serializable
+ class Connector // ...
+
+ @Serializable
+ class Continuation // ...
+
+ @Serializable
+ class ActionBlock // ...
+
+ @Serializable
+ class VendorElement // ...
+
+ @Serializable
+ class Position(
+ val x: Int,
+ val y: Int
+ )
+
+ @Serializable
+ class Block(
+ val position: Position,
+ val inputVariables: InOutVariables,
+ val inOutVariables: InOutVariables,
+ val outputVariables: InOutVariables,
+ val addData: AddData?,
+ val documentation: Documentation?,
+ val localId: Long,
+ val width: Long?,
+ val height: Long?,
+ private val typeName: String,
+ private val instanceName: String?,
+ val executionOrderId: Long?,
+ val globalId: String?
+ ) {
+ fun getName() = instanceName ?: "UNNAMED_$localId"
+ fun getType() = convertBlockType(typeName)
+
+ @Serializable
+ class InOutVariables(
+ val variableList: List
+ )
+
+ @Serializable
+ class InOutVariable(
+ val connectionPointIn: ConnectionPointIn?,
+ val connectionPointOut: ConnectionPointOut?,
+ val documentation: Documentation?,
+ val formalParameter: String,
+ val negated: Boolean?,
+ val edge: String?,
+ val storage: String?,
+ val hidden: Boolean?
+ )
+ }
+
+ @Serializable
+ class ConnectionPointIn(
+ val relPosition: Position?,
+ val connectionList: List,
+ val expression: ElementNode?,
+ val addData: AddData?,
+ val globalId: String?
+ )
+
+ @Serializable
+ class ConnectionPointOut(
+ val relPosition: Position?,
+ val expression: ElementNode?,
+ val addData: AddData?,
+ val globalId: String?
+ )
+
+ @Serializable
+ class Connection(
+ val positionList: List,
+ val addData: AddData?,
+ val globalId: String?,
+ val refLocalId: Long,
+ val formalParameter: String?
+ )
+
+ @Serializable
+ class Label // ...
+
+ @Serializable
+ class Jump // ...
+
+ @Serializable
+ class Return // ...
+
+ @Serializable
+ class ElementNode(
+ @XmlValue(true)
+ private val text: String
+ ) {
+ fun getText(): String {
+ return text
+ }
+ }
+
+ @Serializable
+ class FBD(
+ val commentList: List,
+ val errorList: List,
+ val connectorList: List,
+ val continuationList: List,
+ val actionBlockList: List,
+ val vendorElementList: List,
+ val blockList: List,
+ val inVariableList: List,
+ val outVariableList: List,
+ val inOutVariableList: List,
+ val labelList: List