From ccc9d0249f1c13af3554c67f8d52181bb7504b61 Mon Sep 17 00:00:00 2001 From: Egor Malko Date: Sat, 10 Jun 2023 01:54:41 +0300 Subject: [PATCH 1/2] IEC61131 integration snapshot, not launching because of error --- .idea/aws.xml | 17 + .idea/misc.xml | 3 + code/iec61131-integration/.module-id | 1 + code/iec61131-integration/build.gradle.kts | 39 ++ .../importer/Iec61131ImportLocationConfig.kt | 33 ++ .../importer/Iec61131IntegrationIcons.kt | 9 + .../importer/Iec61131ProjectTemplate.kt | 51 +++ .../src/main/resources/META-INF/plugin.xml | 22 + .../main/resources/icons/iec61131_import.svg | 5 + .../src/test/resources/ParserTestFbt1.fbt | 6 + code/iec61131/build.gradle.kts | 10 + .../lib/iec61131/converter/ConverterBase.kt | 59 +++ .../converter/FbInterfaceConverter.kt | 56 +++ .../iec61131/converter/FbNetworkConverter.kt | 159 +++++++ .../lib/iec61131/converter/FbTranslator.kt | 222 ++++++++++ .../fbme/lib/iec61131/converter/FbdInfo.kt | 9 + .../converter/Iec61131ConverterException.kt | 16 + .../lib/iec61131/converter/NetworkPart.kt | 24 ++ .../iec61131/converter/ProjectConverter.kt | 40 ++ .../lib/iec61131/converter/SystemConverter.kt | 128 ++++++ .../org/fbme/lib/iec61131/converter/Utils.kt | 26 ++ .../org/fbme/lib/iec61131/model/BlocksInfo.kt | 309 ++++++++++++++ .../fbme/lib/iec61131/model/Iec61131Xml.kt | 396 ++++++++++++++++++ .../lib/iec61131/model/Iec61131XmlPolicy.kt | 35 ++ .../service/BlockInConnectionsService.kt | 39 ++ .../lib/iec61131/service/FbdBlockService.kt | 13 + .../service/FbdEvaluationOrderService.kt | 54 +++ .../iec61131/service/FbdVariableService.kt | 124 ++++++ .../lib/iec61131/service/InterfaceService.kt | 24 ++ settings.gradle.kts | 4 +- 30 files changed, 1932 insertions(+), 1 deletion(-) create mode 100644 .idea/aws.xml create mode 100644 code/iec61131-integration/.module-id create mode 100644 code/iec61131-integration/build.gradle.kts create mode 100644 code/iec61131-integration/src/main/kotlin/org/fbme/integration/iec61131/importer/Iec61131ImportLocationConfig.kt create mode 100644 code/iec61131-integration/src/main/kotlin/org/fbme/integration/iec61131/importer/Iec61131IntegrationIcons.kt create mode 100644 code/iec61131-integration/src/main/kotlin/org/fbme/integration/iec61131/importer/Iec61131ProjectTemplate.kt create mode 100644 code/iec61131-integration/src/main/resources/META-INF/plugin.xml create mode 100644 code/iec61131-integration/src/main/resources/icons/iec61131_import.svg create mode 100644 code/iec61131-integration/src/test/resources/ParserTestFbt1.fbt create mode 100644 code/iec61131/build.gradle.kts create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/ConverterBase.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbInterfaceConverter.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbNetworkConverter.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbTranslator.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbdInfo.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/Iec61131ConverterException.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/NetworkPart.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/ProjectConverter.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/SystemConverter.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/Utils.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/model/BlocksInfo.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/model/Iec61131Xml.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/model/Iec61131XmlPolicy.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/service/BlockInConnectionsService.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/service/FbdBlockService.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/service/FbdEvaluationOrderService.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/service/FbdVariableService.kt create mode 100644 code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/service/InterfaceService.kt diff --git a/.idea/aws.xml b/.idea/aws.xml new file mode 100644 index 000000000..03f1bb6ee --- /dev/null +++ b/.idea/aws.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 4981e477a..5fb87a1b1 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -14,4 +14,7 @@ + + \ No newline at end of file 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 @@ + + + + 61131 + \ 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..a053dde21 --- /dev/null +++ b/code/iec61131/src/main/kotlin/org/fbme/lib/iec61131/converter/FbNetworkConverter.kt @@ -0,0 +1,159 @@ +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, + startEvent: String = "REQ", + 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)) + } + endpointCoordinates["REQ"] = createEndpointCoordinate("REQ", 0, 0) + 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 \ 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 index a053dde21..a7ac493ca 100644 --- 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 @@ -7,8 +7,8 @@ import kotlin.random.Random class FbNetworkConverter( fbdInfo: FbdInfo, converterArguments: ConverterArguments, - startEvent: String = "REQ", - endEvent: String? = "CNF" + private val startEvent: String = "REQ", + private val endEvent: String? = "CNF" ) : ConverterBase(converterArguments) { private val networkEventConverter = @@ -137,8 +137,10 @@ class FbNetworkConverter( val varName = outputVariables[i] endpointCoordinates[varName] = createEndpointCoordinate(varName, 500 + (blocksNumber + 3) * 500, 100 * (i + 1)) } - endpointCoordinates["REQ"] = createEndpointCoordinate("REQ", 0, 0) - endpointCoordinates["CNF"] = createEndpointCoordinate("CNF", 500 + (blocksNumber + 3) * 500, 0) + if (startEvent == "REQ") + endpointCoordinates["REQ"] = createEndpointCoordinate("REQ", 0, 0) + if (endEvent == "CNF") + endpointCoordinates["CNF"] = createEndpointCoordinate("CNF", 500 + (blocksNumber + 3) * 500, 0) return endpointCoordinates }