From fcde587ae0255f4c510e4067c4c384a93ff84337 Mon Sep 17 00:00:00 2001 From: Valentina Danilova Date: Thu, 5 May 2022 20:54:29 +0300 Subject: [PATCH 01/17] declaration --- .idea/runConfigurations.xml | 10 - .../org.fbme.ide.integration.fordiac.msd | 2 - .../org.fbme.ide.enas.lang.mpl | 1 - .../org.fbme.ide.enas.analyzer.msd | 1 - .../models/org.fbme.language.build.mps | 12 +- .../org.fbme.ide.attributes.mpl | 4 +- ...iec61499.lang.generator.main@generator.mps | 1 + .../org.fbme.ide.iec61499.lang.behavior.mps | 6 + .../org.fbme.ide.iec61499.lang.editor.mps | 22 ++ .../org.fbme.ide.iec61499.lang.structure.mps | 140 ++++++++ .../org.fbme.ide.iec61499.lang.mpl | 4 - .../org.fbme.ide.meta.editor.mpl | 3 - .../org.fbme.ide.st.lang.mpl | 3 - .../org.fbme.ide.util.lang.mpl | 1 - ...bme.ide.iec61499.adapter.interfacepart.mps | 340 ++++++++++++++++++ .../org.fbme.ide.iec61499.repository.mps | 48 +++ .../org.fbme.ide.iec61499.adapter.msd | 4 +- code/library/org.fbme.lib.msd | 1 - .../org/fbme/lib/iec61499/IEC61499Factory.kt | 1 + .../fbme/lib/iec61499/parser/RootConverter.kt | 4 + .../stringify/RootDeclarationPrinter.kt | 1 + .../models/org.fbme.platform.build.mps | 3 +- .../org.fbme.ide.platform.persistence.mps | 158 +++++--- .../org.fbme.ide.platform.projectWizard.mps | 2 +- .../org.fbme.ide.platform.msd | 5 +- .../org.fbme.ide.richediting.lang.mpl | 4 - .../org.fbme.ide.richediting.msd | 3 - ...e.iec61499.lang.sandbox.mpsPersistence.mps | 6 + 28 files changed, 694 insertions(+), 96 deletions(-) delete mode 100644 .idea/runConfigurations.xml diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 797acea53..000000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/code/4diac-integration/solutions/org.fbme.ide.integration.fordiac/org.fbme.ide.integration.fordiac.msd b/code/4diac-integration/solutions/org.fbme.ide.integration.fordiac/org.fbme.ide.integration.fordiac.msd index 812bf3bf0..688cd966e 100644 --- a/code/4diac-integration/solutions/org.fbme.ide.integration.fordiac/org.fbme.ide.integration.fordiac.msd +++ b/code/4diac-integration/solutions/org.fbme.ide.integration.fordiac/org.fbme.ide.integration.fordiac.msd @@ -41,7 +41,6 @@ - @@ -70,7 +69,6 @@ - diff --git a/code/enas/languages/org.fbme.ide.enas.lang/org.fbme.ide.enas.lang.mpl b/code/enas/languages/org.fbme.ide.enas.lang/org.fbme.ide.enas.lang.mpl index 914089d7e..c09c763b5 100644 --- a/code/enas/languages/org.fbme.ide.enas.lang/org.fbme.ide.enas.lang.mpl +++ b/code/enas/languages/org.fbme.ide.enas.lang/org.fbme.ide.enas.lang.mpl @@ -97,7 +97,6 @@ - diff --git a/code/enas/solutions/org.fbme.ide.enas.analyzer/org.fbme.ide.enas.analyzer.msd b/code/enas/solutions/org.fbme.ide.enas.analyzer/org.fbme.ide.enas.analyzer.msd index bb81bf1e5..66726340f 100644 --- a/code/enas/solutions/org.fbme.ide.enas.analyzer/org.fbme.ide.enas.analyzer.msd +++ b/code/enas/solutions/org.fbme.ide.enas.analyzer/org.fbme.ide.enas.analyzer.msd @@ -34,7 +34,6 @@ - diff --git a/code/language/buildsolution/models/org.fbme.language.build.mps b/code/language/buildsolution/models/org.fbme.language.build.mps index ae10dbb23..119f29018 100644 --- a/code/language/buildsolution/models/org.fbme.language.build.mps +++ b/code/language/buildsolution/models/org.fbme.language.build.mps @@ -91,6 +91,7 @@ + @@ -285,6 +286,7 @@ + @@ -321,11 +323,6 @@ - - - - - @@ -836,11 +833,6 @@ - - - - - diff --git a/code/language/languages/org.fbme.ide.attributes/org.fbme.ide.attributes.mpl b/code/language/languages/org.fbme.ide.attributes/org.fbme.ide.attributes.mpl index f0fff8c64..a13769751 100644 --- a/code/language/languages/org.fbme.ide.attributes/org.fbme.ide.attributes.mpl +++ b/code/language/languages/org.fbme.ide.attributes/org.fbme.ide.attributes.mpl @@ -98,10 +98,12 @@ + + - + diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/generator/template/org.fbme.ide.iec61499.lang.generator.main@generator.mps b/code/language/languages/org.fbme.ide.iec61499.lang/generator/template/org.fbme.ide.iec61499.lang.generator.main@generator.mps index 0d483cb3c..3cd1cdb50 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/generator/template/org.fbme.ide.iec61499.lang.generator.main@generator.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/generator/template/org.fbme.ide.iec61499.lang.generator.main@generator.mps @@ -3,6 +3,7 @@ + diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.behavior.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.behavior.mps index 726abe59b..1230b013e 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.behavior.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.behavior.mps @@ -4575,5 +4575,11 @@ + + + + + + diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps index f84cf3fb0..a0a0bf531 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps @@ -4349,5 +4349,27 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps index 024e980e3..d227c5619 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps @@ -1659,5 +1659,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/org.fbme.ide.iec61499.lang.mpl b/code/language/languages/org.fbme.ide.iec61499.lang/org.fbme.ide.iec61499.lang.mpl index 36a249129..0f237762b 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/org.fbme.ide.iec61499.lang.mpl +++ b/code/language/languages/org.fbme.ide.iec61499.lang/org.fbme.ide.iec61499.lang.mpl @@ -53,7 +53,6 @@ - @@ -86,7 +85,6 @@ - @@ -103,7 +101,6 @@ - @@ -131,7 +128,6 @@ - diff --git a/code/language/languages/org.fbme.ide.meta.editor/org.fbme.ide.meta.editor.mpl b/code/language/languages/org.fbme.ide.meta.editor/org.fbme.ide.meta.editor.mpl index cf8d5f739..a3d761735 100644 --- a/code/language/languages/org.fbme.ide.meta.editor/org.fbme.ide.meta.editor.mpl +++ b/code/language/languages/org.fbme.ide.meta.editor/org.fbme.ide.meta.editor.mpl @@ -142,7 +142,6 @@ - @@ -158,7 +157,6 @@ - @@ -194,7 +192,6 @@ - diff --git a/code/language/languages/org.fbme.ide.st.lang/org.fbme.ide.st.lang.mpl b/code/language/languages/org.fbme.ide.st.lang/org.fbme.ide.st.lang.mpl index 4029d747e..bd112958f 100644 --- a/code/language/languages/org.fbme.ide.st.lang/org.fbme.ide.st.lang.mpl +++ b/code/language/languages/org.fbme.ide.st.lang/org.fbme.ide.st.lang.mpl @@ -80,14 +80,12 @@ - - @@ -105,7 +103,6 @@ - diff --git a/code/language/languages/org.fbme.ide.util.lang/org.fbme.ide.util.lang.mpl b/code/language/languages/org.fbme.ide.util.lang/org.fbme.ide.util.lang.mpl index e28f8489c..3d1e0b10d 100644 --- a/code/language/languages/org.fbme.ide.util.lang/org.fbme.ide.util.lang.mpl +++ b/code/language/languages/org.fbme.ide.util.lang/org.fbme.ide.util.lang.mpl @@ -53,7 +53,6 @@ - diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps index 5acb82c1d..60e49281d 100644 --- a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps +++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps @@ -7539,5 +7539,345 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.repository.mps b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.repository.mps index 2ccca6507..a7fd18600 100644 --- a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.repository.mps +++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.repository.mps @@ -307,6 +307,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/org.fbme.ide.iec61499.adapter.msd b/code/language/solutions/org.fbme.ide.iec61499.adapter/org.fbme.ide.iec61499.adapter.msd index ffcdaf254..e8b9bdfb2 100644 --- a/code/language/solutions/org.fbme.ide.iec61499.adapter/org.fbme.ide.iec61499.adapter.msd +++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/org.fbme.ide.iec61499.adapter.msd @@ -23,7 +23,7 @@ 8865b7a8-5271-43d3-884c-6fd1d9cfdd34(MPS.OpenAPI) 3f233e7f-b8a6-46d2-a57f-795d56775243(Annotations) 6594f340-4d73-4027-b7d3-c6ca2e70a53b(org.fbme.ide.iec61499.lang) - 1db6de07-b355-4c0f-9979-75b4ac1e8215(org.fbme.lib) + 1db6de07-b355-4c0f-9979-75b4ac1e8215(org.fbme.lib) 14f8fb68-9526-41ae-a986-e33a7382fe12(org.fbme.ide.util.lang) 6ed54515-acc8-4d1e-a16c-9fd6cfe951ea(MPS.Core) 491a88b6-52bd-4c4e-a61a-8496046b69aa(org.fbme.ide.attributes) @@ -37,7 +37,6 @@ - @@ -55,7 +54,6 @@ - diff --git a/code/library/org.fbme.lib.msd b/code/library/org.fbme.lib.msd index 5b109d71a..4ceeb26ce 100644 --- a/code/library/org.fbme.lib.msd +++ b/code/library/org.fbme.lib.msd @@ -31,7 +31,6 @@ - diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/IEC61499Factory.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/IEC61499Factory.kt index 0f4c938b8..8169e2b25 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/IEC61499Factory.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/IEC61499Factory.kt @@ -18,6 +18,7 @@ interface IEC61499Factory { fun createApplicationDeclaration(identifier: Identifier?): ApplicationDeclaration fun createBasicFBTypeDeclaration(identifier: Identifier?): BasicFBTypeDeclaration fun createCompositeFBTypeDeclaration(identifier: Identifier?): CompositeFBTypeDeclaration + fun createCATBlockTypeDeclaration(identifier: Identifier?): CATBlockTypeDeclaration fun createDeviceDeclaration(identifier: Identifier?): DeviceDeclaration fun createDeviceTypeDeclaration(identifier: Identifier?): DeviceTypeDeclaration fun createParameterAssignment(): ParameterAssignment diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/RootConverter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/RootConverter.kt index 846047d7f..f88e60a35 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/RootConverter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/RootConverter.kt @@ -47,6 +47,10 @@ class RootConverter( return SystemConverter(arguments()).extract() } + fun convertCATConfiguration(): CATBlockTypeDeclaration { + return CATBlockTypeConverter(arguments()).extract() + } + private fun arguments(): ConverterArgumentsHolder { return ConverterArgumentsHolder(myFactory, myStFactory, myLocus, myDocument.rootElement) } diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/RootDeclarationPrinter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/RootDeclarationPrinter.kt index 240dbf903..1e148e158 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/RootDeclarationPrinter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/RootDeclarationPrinter.kt @@ -12,6 +12,7 @@ class RootDeclarationPrinter(private val myDeclaration: Declaration) { is AdapterTypeDeclaration -> AdapterTypePrinter(myDeclaration).print() is BasicFBTypeDeclaration -> BasicFBTypePrinter(myDeclaration).print() is CompositeFBTypeDeclaration -> CompositeFBTypePrinter(myDeclaration).print() + is CATBlockTypeDeclaration -> CATBlockTypePrinter(myDeclaration).print() is DeviceTypeDeclaration -> DeviceTypePrinter(myDeclaration).print() is ResourceTypeDeclaration -> ResourceTypePrinter(myDeclaration).print() is ServiceInterfaceFBTypeDeclaration -> ServiceInterfaceFBTypePrinter(myDeclaration).print() diff --git a/code/platform/buildsolution/models/org.fbme.platform.build.mps b/code/platform/buildsolution/models/org.fbme.platform.build.mps index de8423fd8..dd39f2612 100644 --- a/code/platform/buildsolution/models/org.fbme.platform.build.mps +++ b/code/platform/buildsolution/models/org.fbme.platform.build.mps @@ -256,9 +256,9 @@ - + @@ -351,6 +351,7 @@ + diff --git a/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.persistence.mps b/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.persistence.mps index 90631f7c8..244796f43 100644 --- a/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.persistence.mps +++ b/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.persistence.mps @@ -609,6 +609,19 @@ + + + + + + + + + + + + + @@ -658,70 +671,80 @@ - - - - + + + + - + - - - - - - - - + + + + + + + + + + + + + - - + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -3790,6 +3813,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3960,6 +4013,25 @@ + + + + + + + + + + + + + + + + + + + @@ -6456,14 +6528,6 @@ - - - - - - - - @@ -6487,6 +6551,14 @@ + + + + + + + + diff --git a/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.projectWizard.mps b/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.projectWizard.mps index c7ed31827..c09dc118f 100644 --- a/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.projectWizard.mps +++ b/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.projectWizard.mps @@ -359,7 +359,7 @@ - + diff --git a/code/platform/solutions/org.fbme.ide.platform/org.fbme.ide.platform.msd b/code/platform/solutions/org.fbme.ide.platform/org.fbme.ide.platform.msd index 1c83b7a64..35271484e 100644 --- a/code/platform/solutions/org.fbme.ide.platform/org.fbme.ide.platform.msd +++ b/code/platform/solutions/org.fbme.ide.platform/org.fbme.ide.platform.msd @@ -27,7 +27,7 @@ 5fa23c0a-216d-4571-a163-e286643e6f5f(jetbrains.mps.generator) 479c7a8c-02f9-43b5-9139-d910cb22f298(jetbrains.mps.core.xml) 1db6de07-b355-4c0f-9979-75b4ac1e8215(org.fbme.lib) - 86441d7a-e194-42da-81a5-2161ec62a379(MPS.Workbench) + 86441d7a-e194-42da-81a5-2161ec62a379(MPS.Workbench) 1ed103c3-3aa6-49b7-9c21-6765ee11f224(MPS.Editor) 25092e07-e655-497c-92fb-558a8e3080ed(jetbrains.mps.ide.ui) 6ed54515-acc8-4d1e-a16c-9fd6cfe951ea(MPS.Core) @@ -40,7 +40,6 @@ - @@ -49,7 +48,6 @@ - @@ -75,7 +73,6 @@ - diff --git a/code/richediting/languages/org.fbme.ide.richediting.lang/org.fbme.ide.richediting.lang.mpl b/code/richediting/languages/org.fbme.ide.richediting.lang/org.fbme.ide.richediting.lang.mpl index 13e4e4b39..e07a1fd57 100644 --- a/code/richediting/languages/org.fbme.ide.richediting.lang/org.fbme.ide.richediting.lang.mpl +++ b/code/richediting/languages/org.fbme.ide.richediting.lang/org.fbme.ide.richediting.lang.mpl @@ -36,7 +36,6 @@ - @@ -47,7 +46,6 @@ - @@ -56,7 +54,6 @@ - @@ -74,7 +71,6 @@ - diff --git a/code/richediting/solutions/org.fbme.ide.richediting/org.fbme.ide.richediting.msd b/code/richediting/solutions/org.fbme.ide.richediting/org.fbme.ide.richediting.msd index 1c96ac24a..1bfafbd26 100644 --- a/code/richediting/solutions/org.fbme.ide.richediting/org.fbme.ide.richediting.msd +++ b/code/richediting/solutions/org.fbme.ide.richediting/org.fbme.ide.richediting.msd @@ -62,7 +62,6 @@ - @@ -86,13 +85,11 @@ - - diff --git a/samples/sandbox/solutions/org.fbme.ide.sandbox/models/org.fbme.ide.iec61499.lang.sandbox.mpsPersistence.mps b/samples/sandbox/solutions/org.fbme.ide.sandbox/models/org.fbme.ide.iec61499.lang.sandbox.mpsPersistence.mps index c9a32f9b0..12753822b 100644 --- a/samples/sandbox/solutions/org.fbme.ide.sandbox/models/org.fbme.ide.iec61499.lang.sandbox.mpsPersistence.mps +++ b/samples/sandbox/solutions/org.fbme.ide.sandbox/models/org.fbme.ide.iec61499.lang.sandbox.mpsPersistence.mps @@ -1534,5 +1534,11 @@ + + + + + + From 19a1820f00e615b7a40b36f78526e93b3771bd30 Mon Sep 17 00:00:00 2001 From: Valentina Danilova Date: Fri, 5 Aug 2022 16:58:47 +0300 Subject: [PATCH 02/17] problem with build --- .idea/ant.xml | 6 ++++++ .idea/codeStyles/Project.xml | 7 +++++++ .idea/compiler.xml | 19 +------------------ .idea/libraries/ant_antlr.xml | 9 +++++++++ .idea/libraries/ant_launcher.xml | 9 +++++++++ .idea/libraries/apache_ant_junit.xml | 14 ++++++++++++++ .idea/misc.xml | 2 +- build-bootstrap.xml | 19 +++++++++++++------ .../models/org.fbme.language.build.mps | 4 +++- .../org.fbme.ide.iec61499.lang.structure.mps | 1 + .../models/org.fbme.platform.build.mps | 1 - 11 files changed, 64 insertions(+), 27 deletions(-) create mode 100644 .idea/ant.xml create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/libraries/ant_antlr.xml create mode 100644 .idea/libraries/ant_launcher.xml create mode 100644 .idea/libraries/apache_ant_junit.xml diff --git a/.idea/ant.xml b/.idea/ant.xml new file mode 100644 index 000000000..a2a476982 --- /dev/null +++ b/.idea/ant.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 000000000..919ce1f1f --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml index c8952aa4d..fb7f4a8a4 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,23 +1,6 @@ - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/.idea/libraries/ant_antlr.xml b/.idea/libraries/ant_antlr.xml new file mode 100644 index 000000000..59652667b --- /dev/null +++ b/.idea/libraries/ant_antlr.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/ant_launcher.xml b/.idea/libraries/ant_launcher.xml new file mode 100644 index 000000000..ef0794b5f --- /dev/null +++ b/.idea/libraries/ant_launcher.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/apache_ant_junit.xml b/.idea/libraries/apache_ant_junit.xml new file mode 100644 index 000000000..9b11bfcbb --- /dev/null +++ b/.idea/libraries/apache_ant_junit.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 5d9825616..e36899607 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,7 +4,7 @@ - + \ No newline at end of file diff --git a/build-bootstrap.xml b/build-bootstrap.xml index edff3b90f..056a9f206 100644 --- a/build-bootstrap.xml +++ b/build-bootstrap.xml @@ -40,9 +40,16 @@ - + + + + + + + + mps.build.number=${mps.build.number}${line.separator}mps.date=${mps.date}${line.separator}mps.build.vcs.number=${mps.build.vcs.number}${line.separator}mps.teamcity.buildConfName=${mps.teamcity.buildConfName}${line.separator}mps.idea.platform.build.number=${mps.idea.platform.build.number}${line.separator}mps.mps.build.counter=${mps.mps.build.counter}${line.separator}mpsBootstrapCore.version.major=${mpsBootstrapCore.version.major}${line.separator}mpsBootstrapCore.version.minor=${mpsBootstrapCore.version.minor}${line.separator}mpsBootstrapCore.version.bugfixNr=${mpsBootstrapCore.version.bugfixNr}${line.separator}mpsBootstrapCore.version.eap=${mpsBootstrapCore.version.eap}${line.separator}mpsBootstrapCore.version=${mpsBootstrapCore.version}${line.separator}fbme_platform.ide-version=${fbme_platform.ide-version}${line.separator}fbme_language.ide-version=${fbme_language.ide-version}${line.separator}fbme_richediting.ide-version=${fbme_richediting.ide-version}${line.separator}fbme_fordiac.ide-version=${fbme_fordiac.ide-version}${line.separator}fbme_smvdebugger.ide-version=${fbme_smvdebugger.ide-version}${line.separator}fbme_nxt.ide-version=${fbme_nxt.ide-version} @@ -69,7 +76,8 @@ - + + @@ -88,7 +96,9 @@ + + @@ -135,7 +145,6 @@ - @@ -174,9 +183,7 @@ - - - + diff --git a/code/language/buildsolution/models/org.fbme.language.build.mps b/code/language/buildsolution/models/org.fbme.language.build.mps index d56eb6b33..fc95c7f10 100644 --- a/code/language/buildsolution/models/org.fbme.language.build.mps +++ b/code/language/buildsolution/models/org.fbme.language.build.mps @@ -83,6 +83,7 @@ + @@ -167,9 +168,9 @@ - + @@ -259,6 +260,7 @@ + diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps index 954fc515e..8fe8929e9 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps @@ -1719,6 +1719,7 @@ + diff --git a/code/platform/buildsolution/models/org.fbme.platform.build.mps b/code/platform/buildsolution/models/org.fbme.platform.build.mps index 919aeda3b..cb7617f90 100644 --- a/code/platform/buildsolution/models/org.fbme.platform.build.mps +++ b/code/platform/buildsolution/models/org.fbme.platform.build.mps @@ -229,7 +229,6 @@ - From 42a2ecdf5c2fb5e099203a92eaea88dafff6d0f9 Mon Sep 17 00:00:00 2001 From: "radimir.sorokin" Date: Fri, 5 Aug 2022 16:41:31 +0200 Subject: [PATCH 03/17] Regenerate build-bootstrap.xml, specify right jdk in project settings --- .idea/misc.xml | 2 +- build-bootstrap.xml | 19 ++++++------------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index e36899607..5d9825616 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,7 +4,7 @@ - + \ No newline at end of file diff --git a/build-bootstrap.xml b/build-bootstrap.xml index 056a9f206..edff3b90f 100644 --- a/build-bootstrap.xml +++ b/build-bootstrap.xml @@ -40,16 +40,9 @@ - - - - + - - - - mps.build.number=${mps.build.number}${line.separator}mps.date=${mps.date}${line.separator}mps.build.vcs.number=${mps.build.vcs.number}${line.separator}mps.teamcity.buildConfName=${mps.teamcity.buildConfName}${line.separator}mps.idea.platform.build.number=${mps.idea.platform.build.number}${line.separator}mps.mps.build.counter=${mps.mps.build.counter}${line.separator}mpsBootstrapCore.version.major=${mpsBootstrapCore.version.major}${line.separator}mpsBootstrapCore.version.minor=${mpsBootstrapCore.version.minor}${line.separator}mpsBootstrapCore.version.bugfixNr=${mpsBootstrapCore.version.bugfixNr}${line.separator}mpsBootstrapCore.version.eap=${mpsBootstrapCore.version.eap}${line.separator}mpsBootstrapCore.version=${mpsBootstrapCore.version}${line.separator}fbme_platform.ide-version=${fbme_platform.ide-version}${line.separator}fbme_language.ide-version=${fbme_language.ide-version}${line.separator}fbme_richediting.ide-version=${fbme_richediting.ide-version}${line.separator}fbme_fordiac.ide-version=${fbme_fordiac.ide-version}${line.separator}fbme_smvdebugger.ide-version=${fbme_smvdebugger.ide-version}${line.separator}fbme_nxt.ide-version=${fbme_nxt.ide-version} @@ -76,8 +69,7 @@ - - + @@ -96,9 +88,7 @@ - - @@ -145,6 +135,7 @@ + @@ -183,7 +174,9 @@ - + + + From 4e96af1890d3bd2c91506d73b92901df91197482 Mon Sep 17 00:00:00 2001 From: Valentina Danilova Date: Fri, 5 Aug 2022 19:10:22 +0300 Subject: [PATCH 04/17] ignored? declarations --- .../declarations/CATBlockTypeDeclaration.kt | 10 +++++++ .../iec61499/parser/CATBlockTypeConverter.kt | 28 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/CATBlockTypeDeclaration.kt create mode 100644 code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/CATBlockTypeConverter.kt diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/CATBlockTypeDeclaration.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/CATBlockTypeDeclaration.kt new file mode 100644 index 000000000..e8b18d6e6 --- /dev/null +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/CATBlockTypeDeclaration.kt @@ -0,0 +1,10 @@ +package org.fbme.lib.iec61499.declarations + +import org.fbme.lib.common.Declaration +import org.fbme.lib.common.RootElement + +interface CATBlockTypeDeclaration : Declaration, RootElement { + val hmiInterface: HMIInterfaceTypeDeclaration + val subCATs: List + var blockDeclaration: CompositeFBTypeDeclaration +} \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/CATBlockTypeConverter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/CATBlockTypeConverter.kt new file mode 100644 index 000000000..c6bdc4adb --- /dev/null +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/CATBlockTypeConverter.kt @@ -0,0 +1,28 @@ +package org.fbme.lib.iec61499.parser + +import org.fbme.lib.common.Identifier +import org.fbme.lib.common.Reference +import org.fbme.lib.iec61499.declarations.CATBlockTypeDeclaration +import org.fbme.lib.iec61499.declarations.CompositeFBTypeDeclaration +import org.fbme.lib.iec61499.fbnetwork.FunctionBlockDeclaration + +class CATBlockTypeConverter(arguments: ConverterArguments) : + DeclarationConverterBase(arguments) { + override fun extractDeclarationBody(identifier: Identifier?): CATBlockTypeDeclaration { + checkNotNull(element) + val fbtd = factory.createCATBlockTypeDeclaration(identifier) + fbtd.blockDeclaration = CompositeBlockConverter(with(this.element.getChild("Composite"))).extract() +// FBInterfaceConverter(this, fbtd).extractInterface() +// FBInterfaceAdaptersConverter(this, fbtd).extractAdapters() +// FBNetworkConverter(with(element.getChild("SubCAT")), fbtd.network).extractNetwork() + return fbtd + } + private inner class CompositeBlockConverter(arguments: ConverterArguments) : + DeclarationConverterBase(arguments) { + override fun extractDeclarationBody(identifier: Identifier?): CompositeFBTypeDeclaration { + checkNotNull(element) + val fbd = factory.createCompositeFBTypeDeclaration(identifier) + return fbd + } + } +} \ No newline at end of file From b3ec7131d045bcab3d3fe18517ac744b677badbb Mon Sep 17 00:00:00 2001 From: Valentina Danilova Date: Sat, 6 Aug 2022 05:25:24 +0300 Subject: [PATCH 05/17] . --- .../iec61499/declarations/DependentFilesDefinition.kt | 5 +++++ .../declarations/HMIInterfaceTypeDeclaration.kt | 10 ++++++++++ .../lib/iec61499/declarations/SubCATDeclaration.kt | 8 ++++++++ .../fbme/lib/iec61499/declarations/SymbolDefinition.kt | 5 +++++ 4 files changed, 28 insertions(+) create mode 100644 code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/DependentFilesDefinition.kt create mode 100644 code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/HMIInterfaceTypeDeclaration.kt create mode 100644 code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/SubCATDeclaration.kt create mode 100644 code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/SymbolDefinition.kt diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/DependentFilesDefinition.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/DependentFilesDefinition.kt new file mode 100644 index 000000000..4de6a8b2e --- /dev/null +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/DependentFilesDefinition.kt @@ -0,0 +1,5 @@ +package org.fbme.lib.iec61499.declarations + +interface DependentFilesDefinition { + var value: String +} \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/HMIInterfaceTypeDeclaration.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/HMIInterfaceTypeDeclaration.kt new file mode 100644 index 000000000..9a31b1307 --- /dev/null +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/HMIInterfaceTypeDeclaration.kt @@ -0,0 +1,10 @@ +package org.fbme.lib.iec61499.declarations + +import org.fbme.lib.common.ContainedElement +import org.fbme.lib.common.Declaration +import org.fbme.lib.common.Reference + +interface HMIInterfaceTypeDeclaration : Declaration, ContainedElement { +// val symbol: SymbolDefinition +// val blockDeclaration: Reference +} \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/SubCATDeclaration.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/SubCATDeclaration.kt new file mode 100644 index 000000000..e123165a4 --- /dev/null +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/SubCATDeclaration.kt @@ -0,0 +1,8 @@ +package org.fbme.lib.iec61499.declarations + +import org.fbme.lib.common.ContainedElement +import org.fbme.lib.common.Declaration + +interface SubCATDeclaration: Declaration, ContainedElement { + +} diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/SymbolDefinition.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/SymbolDefinition.kt new file mode 100644 index 000000000..da071c7f3 --- /dev/null +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/SymbolDefinition.kt @@ -0,0 +1,5 @@ +package org.fbme.lib.iec61499.declarations + +interface SymbolDefinition { + var children: List +} \ No newline at end of file From ac0b770e79ce0185927be94f1f0e4370512709c8 Mon Sep 17 00:00:00 2001 From: Valentina Danilova Date: Sat, 6 Aug 2022 05:34:42 +0300 Subject: [PATCH 06/17] . --- .../iec61499/stringify/CATBlockTypePrinter.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/CATBlockTypePrinter.kt diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/CATBlockTypePrinter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/CATBlockTypePrinter.kt new file mode 100644 index 000000000..7b7401803 --- /dev/null +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/CATBlockTypePrinter.kt @@ -0,0 +1,22 @@ +package org.fbme.lib.iec61499.stringify + +import org.fbme.lib.iec61499.declarations.CATBlockTypeDeclaration +import org.fbme.lib.iec61499.declarations.CompositeFBTypeDeclaration +import org.jdom.Element + +class CATBlockTypePrinter(declaration: CATBlockTypeDeclaration) : + DeclarationPrinterBase(declaration, "CAT") { + override fun printDeclarationBody(element: Element) { + element.setAttribute("name", this.element.name) + element.addContent(CompositeBlockPrinter(this.element.blockDeclaration).print()) +// element.addContent(HMIInterfacePrinter(this.element.hmiInterface).print()) +// element.addContent(SubCATPrinter(this.element.subCATs).print()) + } + + private class CompositeBlockPrinter(fb: CompositeFBTypeDeclaration) : + DeclarationPrinterBase(fb, "Composite") { + override fun printDeclarationBody(element: Element) { +// element.setAttribute("Type", this.element.typeReference.presentation) + } + } +} \ No newline at end of file From 790039ffb4bb641918c8097544834fb8dbdb494c Mon Sep 17 00:00:00 2001 From: "radimir.sorokin" Date: Sat, 6 Aug 2022 06:07:24 +0200 Subject: [PATCH 07/17] Fix broken reference --- .../models/org.fbme.ide.platform.projectWizard.mps | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.projectWizard.mps b/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.projectWizard.mps index 87ab6b010..d95bd3591 100644 --- a/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.projectWizard.mps +++ b/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.projectWizard.mps @@ -360,7 +360,7 @@ - + From f2673dfc5d235bf6c64e6179152984d913fffd9b Mon Sep 17 00:00:00 2001 From: Valentina Danilova Date: Fri, 21 Apr 2023 18:46:03 +0300 Subject: [PATCH 08/17] converting --- .idea/compiler.xml | 8 -- .idea/modules/fbme.iml | 8 -- .../org.fbme.ide.iec61499.lang.behavior.mps | 6 - .../org.fbme.ide.iec61499.lang.editor.mps | 130 +++++++++++++++++ .../org.fbme.ide.iec61499.lang.structure.mps | 68 ++++----- ...bme.ide.iec61499.adapter.interfacepart.mps | 136 ++++++++++++------ .../org.fbme.ide.iec61499.repository.mps | 108 ++++++++++++++ .../repository/PlatformDeclarationsScope.kt | 4 + .../fbme/lib/iec61499/DeclarationsScope.kt | 1 + .../declarations/CATBlockTypeDeclaration.kt | 1 - .../HMIInterfaceTypeDeclaration.kt | 5 +- .../fbme/lib/iec61499/parser/RootConverter.kt | 1 + .../org.fbme.ide.platform.persistence.mps | 75 ++++------ 13 files changed, 395 insertions(+), 156 deletions(-) delete mode 100644 .idea/modules/fbme.iml diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 58d13c02c..c8952aa4d 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,14 +1,6 @@ - - - - - - - - diff --git a/.idea/modules/fbme.iml b/.idea/modules/fbme.iml deleted file mode 100644 index f49154055..000000000 --- a/.idea/modules/fbme.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.behavior.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.behavior.mps index 27250743c..e5cb9e719 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.behavior.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.behavior.mps @@ -4599,11 +4599,5 @@ - - - - - - diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps index 032b8336a..2819c72df 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps @@ -4401,6 +4401,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps index cde800b0c..ccfc0f531 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps @@ -1564,46 +1564,23 @@ - - + + - - - + + - - - - - - - - - - - - - - - - - - - - - - @@ -1613,31 +1590,38 @@ - - - - - - - + + + + + + - - + + - - + + + - - + + - + + + + + + + + - + diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps index 59a58fc00..8625fca65 100644 --- a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps +++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps @@ -107,6 +107,7 @@ + @@ -7707,46 +7708,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -7911,6 +7872,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.repository.mps b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.repository.mps index fbd55a6da..f5e7bb020 100644 --- a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.repository.mps +++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.repository.mps @@ -93,6 +93,9 @@ + + + @@ -354,6 +357,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -4048,6 +4090,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformDeclarationsScope.kt b/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformDeclarationsScope.kt index eca6aeb1d..7cc9736b6 100644 --- a/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformDeclarationsScope.kt +++ b/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformDeclarationsScope.kt @@ -17,6 +17,10 @@ internal class PlatformDeclarationsScope( return myRepository.getAdapter(findNode(identifier), CompositeFBTypeDeclaration::class.java) } + override fun findCATBlockTypeDeclaration(identifier: Identifier): CATBlockTypeDeclaration? { + return myRepository.getAdapter(findNode(identifier), CATBlockTypeDeclaration::class.java) + } + override fun findBasicFBTypeDeclaration(identifier: Identifier): BasicFBTypeDeclaration? { return myRepository.getAdapter(findNode(identifier), BasicFBTypeDeclaration::class.java) } diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/DeclarationsScope.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/DeclarationsScope.kt index 3e35d940a..73f634785 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/DeclarationsScope.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/DeclarationsScope.kt @@ -6,6 +6,7 @@ import org.fbme.lib.iec61499.fbnetwork.FunctionBlockDeclaration interface DeclarationsScope { fun findCompositeFBTypeDeclaration(identifier: Identifier): CompositeFBTypeDeclaration? + fun findCATBlockTypeDeclaration(identifier: Identifier): CATBlockTypeDeclaration? fun findBasicFBTypeDeclaration(identifier: Identifier): BasicFBTypeDeclaration? fun findServiceFBTypeDeclaration(identifier: Identifier): ServiceInterfaceFBTypeDeclaration? fun findAdapterTypeDeclaration(identifier: Identifier): AdapterTypeDeclaration? diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/CATBlockTypeDeclaration.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/CATBlockTypeDeclaration.kt index e8b18d6e6..5c96df53c 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/CATBlockTypeDeclaration.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/CATBlockTypeDeclaration.kt @@ -5,6 +5,5 @@ import org.fbme.lib.common.RootElement interface CATBlockTypeDeclaration : Declaration, RootElement { val hmiInterface: HMIInterfaceTypeDeclaration - val subCATs: List var blockDeclaration: CompositeFBTypeDeclaration } \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/HMIInterfaceTypeDeclaration.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/HMIInterfaceTypeDeclaration.kt index 9a31b1307..34eb52794 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/HMIInterfaceTypeDeclaration.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/HMIInterfaceTypeDeclaration.kt @@ -5,6 +5,7 @@ import org.fbme.lib.common.Declaration import org.fbme.lib.common.Reference interface HMIInterfaceTypeDeclaration : Declaration, ContainedElement { -// val symbol: SymbolDefinition -// val blockDeclaration: Reference + val inputs: MutableList + val outputs: MutableList + val source: String } \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/RootConverter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/RootConverter.kt index f2463666f..0165e9143 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/RootConverter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/RootConverter.kt @@ -10,6 +10,7 @@ class RootConverter( ) { fun convertFBType(): FBTypeDeclaration { val root = myDocument.rootElement + var decl = myConfiguration.createBasicFbTypeConverter(arguments()).extract().algorithms.get(0).body!!. if (root.getChild("FBNetwork") != null) { return myConfiguration.createCompositeFbTypeConverter(arguments()).extract() } diff --git a/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.persistence.mps b/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.persistence.mps index 14a464586..dd05a0e1f 100644 --- a/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.persistence.mps +++ b/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.persistence.mps @@ -96,6 +96,7 @@ + @@ -357,6 +358,13 @@ + + + + + + + @@ -3153,6 +3161,24 @@ + + + + + + + + + + + + + + + + + + @@ -3594,36 +3620,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3794,25 +3790,6 @@ - - - - - - - - - - - - - - - - - - - From bc164aa3318ba6008db6f105edc108ebe77d5b73 Mon Sep 17 00:00:00 2001 From: Valentina Danilova Date: Fri, 21 Apr 2023 16:48:34 +0300 Subject: [PATCH 09/17] converters and printers --- .idea/compiler.xml | 8 + .idea/jarRepositories.xml | 46 +- .mps/runConfigurations/MPS_with_IEC_61499.xml | 6 +- .../org.fbme.ide.iec61499.lang.editor.mps | 141 +---- .../org.fbme.ide.iec61499.lang.structure.mps | 120 +---- ...bme.ide.iec61499.adapter.interfacepart.mps | 506 ++++++++---------- .../org/fbme/lib/iec61499/IEC61499Factory.kt | 1 + .../declarations/CATBlockTypeDeclaration.kt | 7 +- .../HMIInterfaceTypeDeclaration.kt | 8 +- .../iec61499/parser/CATBlockTypeConverter.kt | 15 +- .../fbme/lib/iec61499/parser/RootConverter.kt | 4 +- .../iec61499/stringify/CATBlockTypePrinter.kt | 20 +- .../stringify/RootDeclarationPrinter.kt | 4 +- .../hmi-blocks/JSON_DESERIALIZER.fbt | 76 +++ .../main/resources/hmi-blocks/JSON_FORMER.fbt | 67 +++ .../main/resources/hmi-blocks/JSON_PARSER.fbt | 108 ++++ .../resources/hmi-blocks/JSON_SERIALIZER.fbt | 89 +++ .../resources/hmi-blocks/TYPE_DETECTOR.fbt | 60 +++ .../resources/hmi-blocks/TYPE_SERIALIZER.fbt | 94 ++++ .../importer/BasicFbTypeNxtImporterTest.java | 1 + .../org.fbme.ide.platform.persistence.mps | 357 +++++++++++- .../src/test/kotlin/HmiPrinterTest.kt | 36 ++ 22 files changed, 1202 insertions(+), 572 deletions(-) create mode 100644 code/library/src/main/resources/hmi-blocks/JSON_DESERIALIZER.fbt create mode 100644 code/library/src/main/resources/hmi-blocks/JSON_FORMER.fbt create mode 100644 code/library/src/main/resources/hmi-blocks/JSON_PARSER.fbt create mode 100644 code/library/src/main/resources/hmi-blocks/JSON_SERIALIZER.fbt create mode 100644 code/library/src/main/resources/hmi-blocks/TYPE_DETECTOR.fbt create mode 100644 code/library/src/main/resources/hmi-blocks/TYPE_SERIALIZER.fbt create mode 100644 code/platform/src/test/kotlin/HmiPrinterTest.kt diff --git a/.idea/compiler.xml b/.idea/compiler.xml index c8952aa4d..58d13c02c 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,14 @@ + + + + + + + + diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index dc08c64af..121160ee8 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -6,36 +6,26 @@ \ No newline at end of file diff --git a/.mps/runConfigurations/MPS_with_IEC_61499.xml b/.mps/runConfigurations/MPS_with_IEC_61499.xml index 20ed40dd2..d787223d3 100644 --- a/.mps/runConfigurations/MPS_with_IEC_61499.xml +++ b/.mps/runConfigurations/MPS_with_IEC_61499.xml @@ -4,15 +4,13 @@ diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps index 2819c72df..84cdad963 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps @@ -4396,26 +4396,29 @@ - + - - - - - - - + + + + - - - - + + + + - + + + + + + + @@ -4423,113 +4426,13 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps index ccfc0f531..45e2d2b89 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps @@ -1564,122 +1564,40 @@ - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - + + - - - - - - + + - - - - + + + - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - + + + + + + + diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps index 8625fca65..69ef97ce9 100644 --- a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps +++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps @@ -7614,135 +7614,281 @@ - - + + + + - - - + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + - + + + + + + + - - + + + + + - - - - - - - - - - - - + + + + + + + + - - - - - + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - + - + + @@ -7759,76 +7905,14 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + @@ -7851,126 +7935,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/IEC61499Factory.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/IEC61499Factory.kt index 83f63f4b5..99f791479 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/IEC61499Factory.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/IEC61499Factory.kt @@ -19,6 +19,7 @@ interface IEC61499Factory { fun createBasicFBTypeDeclaration(identifier: Identifier?): BasicFBTypeDeclaration fun createCompositeFBTypeDeclaration(identifier: Identifier?): CompositeFBTypeDeclaration fun createCATBlockTypeDeclaration(identifier: Identifier?): CATBlockTypeDeclaration + fun createHMIBlockTypeDeclaration(identifier: Identifier?): HMIInterfaceTypeDeclaration fun createDeviceDeclaration(identifier: Identifier?): DeviceDeclaration fun createDeviceTypeDeclaration(identifier: Identifier?): DeviceTypeDeclaration fun createParameterAssignment(): ParameterAssignment diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/CATBlockTypeDeclaration.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/CATBlockTypeDeclaration.kt index 5c96df53c..c9f27cd00 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/CATBlockTypeDeclaration.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/CATBlockTypeDeclaration.kt @@ -2,8 +2,11 @@ package org.fbme.lib.iec61499.declarations import org.fbme.lib.common.Declaration import org.fbme.lib.common.RootElement +import org.fbme.lib.common.Reference + interface CATBlockTypeDeclaration : Declaration, RootElement { - val hmiInterface: HMIInterfaceTypeDeclaration - var blockDeclaration: CompositeFBTypeDeclaration + val hmiInterface: Reference + val blockDeclaration: Reference + val interfaceFileName: String } \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/HMIInterfaceTypeDeclaration.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/HMIInterfaceTypeDeclaration.kt index 34eb52794..e9bfcdb47 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/HMIInterfaceTypeDeclaration.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/HMIInterfaceTypeDeclaration.kt @@ -1,11 +1,5 @@ package org.fbme.lib.iec61499.declarations -import org.fbme.lib.common.ContainedElement -import org.fbme.lib.common.Declaration -import org.fbme.lib.common.Reference -interface HMIInterfaceTypeDeclaration : Declaration, ContainedElement { - val inputs: MutableList - val outputs: MutableList - val source: String +interface HMIInterfaceTypeDeclaration : FBTypeDeclaration { } \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/CATBlockTypeConverter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/CATBlockTypeConverter.kt index c6bdc4adb..8a9e7525e 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/CATBlockTypeConverter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/CATBlockTypeConverter.kt @@ -11,18 +11,9 @@ class CATBlockTypeConverter(arguments: ConverterArguments) : override fun extractDeclarationBody(identifier: Identifier?): CATBlockTypeDeclaration { checkNotNull(element) val fbtd = factory.createCATBlockTypeDeclaration(identifier) - fbtd.blockDeclaration = CompositeBlockConverter(with(this.element.getChild("Composite"))).extract() -// FBInterfaceConverter(this, fbtd).extractInterface() -// FBInterfaceAdaptersConverter(this, fbtd).extractAdapters() -// FBNetworkConverter(with(element.getChild("SubCAT")), fbtd.network).extractNetwork() + fbtd.blockDeclaration.setTargetName(element.getChild("Composite").getAttributeValue("Type")) + fbtd.hmiInterface.setTargetName(element.getChild("HMI").getAttributeValue("Type")) + return fbtd } - private inner class CompositeBlockConverter(arguments: ConverterArguments) : - DeclarationConverterBase(arguments) { - override fun extractDeclarationBody(identifier: Identifier?): CompositeFBTypeDeclaration { - checkNotNull(element) - val fbd = factory.createCompositeFBTypeDeclaration(identifier) - return fbd - } - } } \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/RootConverter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/RootConverter.kt index 0165e9143..3e4233d51 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/RootConverter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/RootConverter.kt @@ -10,7 +10,9 @@ class RootConverter( ) { fun convertFBType(): FBTypeDeclaration { val root = myDocument.rootElement - var decl = myConfiguration.createBasicFbTypeConverter(arguments()).extract().algorithms.get(0).body!!. + if (root.getAttribute("UsedInCAT") != null && root.getAttribute("UsedInCAT").value == "TRUE") { + return HMIInterfaceConverter(arguments()).extract() + } if (root.getChild("FBNetwork") != null) { return myConfiguration.createCompositeFbTypeConverter(arguments()).extract() } diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/CATBlockTypePrinter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/CATBlockTypePrinter.kt index 7b7401803..15e06b1d6 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/CATBlockTypePrinter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/CATBlockTypePrinter.kt @@ -2,21 +2,29 @@ package org.fbme.lib.iec61499.stringify import org.fbme.lib.iec61499.declarations.CATBlockTypeDeclaration import org.fbme.lib.iec61499.declarations.CompositeFBTypeDeclaration +import org.fbme.lib.iec61499.fbnetwork.FunctionBlockDeclaration import org.jdom.Element class CATBlockTypePrinter(declaration: CATBlockTypeDeclaration) : DeclarationPrinterBase(declaration, "CAT") { override fun printDeclarationBody(element: Element) { element.setAttribute("name", this.element.name) - element.addContent(CompositeBlockPrinter(this.element.blockDeclaration).print()) -// element.addContent(HMIInterfacePrinter(this.element.hmiInterface).print()) -// element.addContent(SubCATPrinter(this.element.subCATs).print()) + element.addContent(CompositePrinter(this.element).print()) + element.addContent(HMIPrinter(this.element).print()) } - private class CompositeBlockPrinter(fb: CompositeFBTypeDeclaration) : - DeclarationPrinterBase(fb, "Composite") { + private class CompositePrinter(fb: CATBlockTypeDeclaration) : + DeclarationPrinterBase(fb, "Composite") { override fun printDeclarationBody(element: Element) { -// element.setAttribute("Type", this.element.typeReference.presentation) + element.setAttribute("Type", this.element.blockDeclaration.presentation) + } + } + + private class HMIPrinter(fb: CATBlockTypeDeclaration) : + DeclarationPrinterBase(fb, "HMI") { + override fun printDeclarationBody(element: Element) { + element.setAttribute("Type", this.element.hmiInterface.presentation) + element.setAttribute("InterfaceFile", this.element.interfaceFileName) } } } \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/RootDeclarationPrinter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/RootDeclarationPrinter.kt index b5f607323..fde14210a 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/RootDeclarationPrinter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/RootDeclarationPrinter.kt @@ -2,17 +2,19 @@ package org.fbme.lib.iec61499.stringify import org.fbme.lib.common.Declaration import org.fbme.lib.iec61499.declarations.* +import org.fbme.lib.iec61499.parser.ConverterArguments import org.jdom.DocType import org.jdom.Document import org.jdom.Element -class RootDeclarationPrinter(private val myDeclaration: Declaration) { +class RootDeclarationPrinter(private val myDeclaration: Declaration, val converterArguments: ConverterArguments) { fun print(): Document { val rootElement: Element = when (myDeclaration) { is AdapterTypeDeclaration -> AdapterTypePrinter(myDeclaration).print() is BasicFBTypeDeclaration -> BasicFBTypePrinter(myDeclaration).print() is CompositeFBTypeDeclaration -> CompositeFBTypePrinter(myDeclaration).print() is CATBlockTypeDeclaration -> CATBlockTypePrinter(myDeclaration).print() + is HMIInterfaceTypeDeclaration -> HMIBlockPrinter(myDeclaration, converterArguments).print() is DeviceTypeDeclaration -> DeviceTypePrinter(myDeclaration).print() is ResourceTypeDeclaration -> ResourceTypePrinter(myDeclaration).print() is ServiceInterfaceFBTypeDeclaration -> ServiceInterfaceFBTypePrinter(myDeclaration).print() diff --git a/code/library/src/main/resources/hmi-blocks/JSON_DESERIALIZER.fbt b/code/library/src/main/resources/hmi-blocks/JSON_DESERIALIZER.fbt new file mode 100644 index 000000000..9d8cc093e --- /dev/null +++ b/code/library/src/main/resources/hmi-blocks/JSON_DESERIALIZER.fbt @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/library/src/main/resources/hmi-blocks/JSON_FORMER.fbt b/code/library/src/main/resources/hmi-blocks/JSON_FORMER.fbt new file mode 100644 index 000000000..c4d253659 --- /dev/null +++ b/code/library/src/main/resources/hmi-blocks/JSON_FORMER.fbt @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/library/src/main/resources/hmi-blocks/JSON_PARSER.fbt b/code/library/src/main/resources/hmi-blocks/JSON_PARSER.fbt new file mode 100644 index 000000000..0ec15d196 --- /dev/null +++ b/code/library/src/main/resources/hmi-blocks/JSON_PARSER.fbt @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/library/src/main/resources/hmi-blocks/JSON_SERIALIZER.fbt b/code/library/src/main/resources/hmi-blocks/JSON_SERIALIZER.fbt new file mode 100644 index 000000000..280f68cd2 --- /dev/null +++ b/code/library/src/main/resources/hmi-blocks/JSON_SERIALIZER.fbt @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/library/src/main/resources/hmi-blocks/TYPE_DETECTOR.fbt b/code/library/src/main/resources/hmi-blocks/TYPE_DETECTOR.fbt new file mode 100644 index 000000000..ea9ffb7af --- /dev/null +++ b/code/library/src/main/resources/hmi-blocks/TYPE_DETECTOR.fbt @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/library/src/main/resources/hmi-blocks/TYPE_SERIALIZER.fbt b/code/library/src/main/resources/hmi-blocks/TYPE_SERIALIZER.fbt new file mode 100644 index 000000000..bdc97c05d --- /dev/null +++ b/code/library/src/main/resources/hmi-blocks/TYPE_SERIALIZER.fbt @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/nxt-integration/src/test/java/org/fbme/integration/nxt/importer/BasicFbTypeNxtImporterTest.java b/code/nxt-integration/src/test/java/org/fbme/integration/nxt/importer/BasicFbTypeNxtImporterTest.java index b61570cd7..91c1579b7 100644 --- a/code/nxt-integration/src/test/java/org/fbme/integration/nxt/importer/BasicFbTypeNxtImporterTest.java +++ b/code/nxt-integration/src/test/java/org/fbme/integration/nxt/importer/BasicFbTypeNxtImporterTest.java @@ -3,6 +3,7 @@ import org.fbme.ide.platform.testing.PlatformTestBase; import org.fbme.ide.platform.testing.PlatformTestRunner; import org.fbme.lib.iec61499.declarations.BasicFBTypeDeclaration; +import org.fbme.lib.iec61499.stringify.BasicFBTypePrinter; import org.fbme.lib.st.types.ElementaryType; import org.junit.Assert; import org.junit.Test; diff --git a/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.persistence.mps b/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.persistence.mps index dd05a0e1f..f42548005 100644 --- a/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.persistence.mps +++ b/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.persistence.mps @@ -96,7 +96,6 @@ - @@ -150,6 +149,9 @@ + + + @@ -358,13 +360,6 @@ - - - - - - - @@ -3024,12 +3019,285 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3061,10 +3329,13 @@ - + + + + @@ -3161,24 +3432,6 @@ - - - - - - - - - - - - - - - - - - @@ -3620,6 +3873,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3657,6 +3940,7 @@ + @@ -3790,6 +4074,25 @@ + + + + + + + + + + + + + + + + + + + diff --git a/code/platform/src/test/kotlin/HmiPrinterTest.kt b/code/platform/src/test/kotlin/HmiPrinterTest.kt new file mode 100644 index 000000000..759a91da3 --- /dev/null +++ b/code/platform/src/test/kotlin/HmiPrinterTest.kt @@ -0,0 +1,36 @@ +import org.fbme.ide.platform.testing.PlatformTestBase +import org.fbme.ide.platform.testing.PlatformTestRunner +import org.fbme.lib.iec61499.parser.STConverter +import org.fbme.lib.iec61499.stringify.HMIInterfaceTypeGenerator.Companion.generateDispatchIn +import org.fbme.lib.st.types.ElementaryType +import org.jdom.Document +import org.jdom.Element +import org.jdom.output.XMLOutputter +import org.junit.Test +import org.junit.runner.RunWith +import java.io.FileOutputStream +import java.io.OutputStream + + +@RunWith(PlatformTestRunner::class) +class HMIPrinterTest : PlatformTestBase() { + @Test + fun parseTest1() { + print("Started") + var fileName = "DISPATCH_IN.fbt" + val resElement: Element = Element("DISPATCH_IN") + var p = factory.createParameterDeclaration(null) + p.type = ElementaryType.STRING + p.name = "COUNT" + p.initialValue = STConverter.parseLiteral(stFactory, "'1'") + +// generateDispatchIn(resElement, factory, stFactory, listOf(p)) +// resElement.print(PrettyPrinter()) + // List dependentList = new DependentDeclarationPrinter(declaration).print(); + val stream: OutputStream = FileOutputStream(fileName) + val xmlOutputter = XMLOutputter() + var doc = Document() + doc.setRootElement(resElement) + xmlOutputter.output(doc, stream) + } +} \ No newline at end of file From 1a598135192612b6da2e595000060870f7ebab06 Mon Sep 17 00:00:00 2001 From: Valentina Danilova Date: Sat, 13 May 2023 14:02:25 +0300 Subject: [PATCH 10/17] . --- .idea/compiler.xml | 8 ------ .../iec61499/parser/HMIInterfaceConverter.kt | 27 +++++++++++++++++++ .../DependentDeclarationGenerator.kt | 23 ++++++++++++++++ .../stringify/HMIInterfaceTypeGenerator.kt | 4 +-- 4 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/HMIInterfaceConverter.kt create mode 100644 code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/DependentDeclarationGenerator.kt diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 58d13c02c..c8952aa4d 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,14 +1,6 @@ - - - - - - - - diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/HMIInterfaceConverter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/HMIInterfaceConverter.kt new file mode 100644 index 000000000..879d7eead --- /dev/null +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/HMIInterfaceConverter.kt @@ -0,0 +1,27 @@ +package org.fbme.lib.iec61499.parser + +import org.fbme.lib.common.Identifier +import org.fbme.lib.common.Reference +import org.fbme.lib.iec61499.declarations.CATBlockTypeDeclaration +import org.fbme.lib.iec61499.declarations.CompositeFBTypeDeclaration +import org.fbme.lib.iec61499.declarations.HMIInterfaceTypeDeclaration +import org.fbme.lib.iec61499.fbnetwork.FunctionBlockDeclaration + +class HMIInterfaceConverter(arguments: ConverterArguments) : + DeclarationConverterBase(arguments) { + override fun extractDeclarationBody(identifier: Identifier?): HMIInterfaceTypeDeclaration { + checkNotNull(element) + val fbtd = factory.createHMIBlockTypeDeclaration(identifier) + val interfaceListElement = element.getChild("InterfaceList") + ParameterDeclarationConverter.extractAll( + with(interfaceListElement.getChild("InputVars")), + fbtd.inputParameters + ) + ParameterDeclarationConverter.extractAll( + with(interfaceListElement.getChild("InputVars")), + fbtd.outputParameters + ) + + return fbtd + } +} \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/DependentDeclarationGenerator.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/DependentDeclarationGenerator.kt new file mode 100644 index 000000000..1cefd2826 --- /dev/null +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/DependentDeclarationGenerator.kt @@ -0,0 +1,23 @@ +package org.fbme.lib.iec61499.stringify + +import org.fbme.lib.common.Declaration +import org.fbme.lib.common.RootElement +import org.fbme.lib.iec61499.declarations.CATBlockTypeDeclaration +import org.fbme.lib.iec61499.declarations.FBTypeDeclaration +import org.fbme.lib.iec61499.declarations.HMIInterfaceTypeDeclaration +import org.fbme.lib.iec61499.parser.ConverterArguments + +class DependentDeclarationGenerator(private val myDeclaration: Declaration, private val converterArguments: ConverterArguments) { + + + fun generate(): List { + val rootElements: List = when (myDeclaration) { + is HMIInterfaceTypeDeclaration -> HMIInterfaceTypeGenerator(myDeclaration, converterArguments).generateDependents() + else -> error("Unrecognized root declaration") + } + + + return rootElements + } + +} \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt index b78854d18..314ff9e0e 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt @@ -159,8 +159,8 @@ companion object { } return STConverter.parseStatementListWithDeclarations( stFactory, - { t, u -> parameterCollector.apply(t, u) }, - code + code, + { t, u -> parameterCollector.apply(t, u) } ) } From a251f0a6ddaf5bda845530c82ae83a08921b83b4 Mon Sep 17 00:00:00 2001 From: Valentina Danilova Date: Sat, 13 May 2023 14:20:01 +0300 Subject: [PATCH 11/17] support new format of persistence --- .../repository/PlatformDeclarationsScope.kt | 2 +- .../DependentDeclarationGenerator.kt | 3 +- .../lib/iec61499/stringify/HMIBlockPrinter.kt | 12 ++++--- .../stringify/HMIInterfaceTypeGenerator.kt | 33 +++++++------------ .../stringify/RootDeclarationPrinter.kt | 3 +- .../persistence/Iec61499ModelFactory.kt | 9 ++++- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformDeclarationsScope.kt b/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformDeclarationsScope.kt index 16a901dab..a0c3f5053 100644 --- a/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformDeclarationsScope.kt +++ b/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformDeclarationsScope.kt @@ -18,7 +18,7 @@ internal class PlatformDeclarationsScope( } override fun findCATBlockTypeDeclaration(identifier: Identifier): CATBlockTypeDeclaration? { - return myRepository.getAdapter(findNode(identifier), CATBlockTypeDeclaration::class.java) + return findNode(identifier)?.let {myRepository.getAdapter(it, CATBlockTypeDeclaration::class.java)} } override fun findBasicFBTypeDeclaration(identifier: Identifier): BasicFBTypeDeclaration? { diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/DependentDeclarationGenerator.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/DependentDeclarationGenerator.kt index 1cefd2826..f9a795f8c 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/DependentDeclarationGenerator.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/DependentDeclarationGenerator.kt @@ -6,8 +6,9 @@ import org.fbme.lib.iec61499.declarations.CATBlockTypeDeclaration import org.fbme.lib.iec61499.declarations.FBTypeDeclaration import org.fbme.lib.iec61499.declarations.HMIInterfaceTypeDeclaration import org.fbme.lib.iec61499.parser.ConverterArguments +import org.fbme.lib.iec61499.parser.Iec61499ConverterConfiguration -class DependentDeclarationGenerator(private val myDeclaration: Declaration, private val converterArguments: ConverterArguments) { +class DependentDeclarationGenerator(private val myDeclaration: Declaration, private val converterArguments: Iec61499ConverterConfiguration) { fun generate(): List { diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIBlockPrinter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIBlockPrinter.kt index 3435cbf7a..25350ebbc 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIBlockPrinter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIBlockPrinter.kt @@ -3,14 +3,16 @@ package org.fbme.lib.iec61499.stringify import org.fbme.lib.iec61499.declarations.CompositeFBTypeDeclaration import org.fbme.lib.iec61499.declarations.HMIInterfaceTypeDeclaration import org.fbme.lib.iec61499.parser.ConverterArguments +import org.fbme.lib.iec61499.parser.Iec61499ConverterConfiguration import org.fbme.lib.iec61499.parser.STConverter import org.fbme.lib.st.types.ElementaryType import org.jdom.Element -class HMIBlockPrinter(declaration: HMIInterfaceTypeDeclaration, val converterArguments: ConverterArguments) : +class HMIBlockPrinter(declaration: HMIInterfaceTypeDeclaration, val converterArguments: Iec61499ConverterConfiguration) : DeclarationPrinterBase(declaration, "FBType") { - + val factory = converterArguments.entryFactory + val stFactory = converterArguments.stEntryFactory override fun printDeclarationBody(element: Element) { element.addContent(FBInterfacePrinterWithAdapters(this.element).print()) val cFB = CompositeFBTypePrinter(generateComposite()).print().children @@ -21,14 +23,14 @@ class HMIBlockPrinter(declaration: HMIInterfaceTypeDeclaration, val converterArg } fun generateComposite(): CompositeFBTypeDeclaration { - var cFB = converterArguments.factory.createCompositeFBTypeDeclaration(null) + var cFB = factory.createCompositeFBTypeDeclaration(null) cFB.name = this.element.name cFB.inputParameters.addAll(this.element.inputParameters) cFB.outputParameters.addAll(this.element.outputParameters) - val initEvent = converterArguments.factory.createEventDeclaration(null) + val initEvent = factory.createEventDeclaration(null) initEvent.name = "INIT" initEvent.associations - val mappingInput = converterArguments.factory.createParameterDeclaration(null) + val mappingInput = factory.createParameterDeclaration(null) mappingInput.name = "MAPPING" mappingInput.type = ElementaryType.STRING cFB.inputEvents.add(initEvent) diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt index 314ff9e0e..d2a5e75dc 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt @@ -8,6 +8,7 @@ import org.fbme.lib.iec61499.fbnetwork.EntryKind import org.fbme.lib.iec61499.fbnetwork.FunctionBlockDeclaration import org.fbme.lib.iec61499.parser.CompositeFBTypeConverter import org.fbme.lib.iec61499.parser.ConverterArguments +import org.fbme.lib.iec61499.parser.Iec61499ConverterConfiguration import org.fbme.lib.iec61499.parser.STConverter import org.fbme.lib.st.STFactory import org.fbme.lib.st.expressions.Expression @@ -17,15 +18,18 @@ import org.fbme.lib.st.types.ElementaryType import org.jdom.Element import java.util.function.BiFunction -class HMIInterfaceTypeGenerator(val declaration: HMIInterfaceTypeDeclaration, val converterArguments: ConverterArguments) { +class HMIInterfaceTypeGenerator(val declaration: HMIInterfaceTypeDeclaration, val converterArguments: Iec61499ConverterConfiguration) { + val factory = converterArguments.entryFactory + val stFactory = converterArguments.stEntryFactory + fun generateDependents(): List { val elements = mutableListOf() - val outFb = generateDispatchOut(converterArguments.factory, converterArguments.stFactory, declaration.outputParameters) + val outFb = generateDispatchOut(factory, stFactory, declaration.outputParameters) elements.add(outFb) val inFb = generateDispatchIn( - converterArguments.factory, - converterArguments.stFactory, + factory, + stFactory, declaration.inputParameters ) elements.add(inFb) @@ -34,21 +38,6 @@ class HMIInterfaceTypeGenerator(val declaration: HMIInterfaceTypeDeclaration, va return elements } - fun printComposite(element: Element, outEl: FunctionBlockDeclaration, inEl: FunctionBlockDeclaration) { - val composite = converterArguments.factory.createCompositeFBTypeDeclaration(null) - composite.network.functionBlocks.add(outEl) - composite.network.functionBlocks.add(inEl) - CompositeFBTypeConverter(converterArguments).extract() -// composite.network.functionBlocks.add(jsonSerializer) -// composite.network.functionBlocks.add(jsonDeserializer) -// composite.network.functionBlocks.add(publish) -// composite.network.functionBlocks.add(subscribe) - val connection = converterArguments.factory.createFBNetworkConnection(EntryKind.EVENT) -// connection.sourceReference(outEl, jsonSerializer) -// connection.sourceReference(inEl, jsonDeserializer) - composite.network.dataConnections - element.addContent(CompositeFBTypePrinter(composite).print()) - } companion object { val CONNECTION_TYPES = listOf( @@ -190,15 +179,15 @@ companion object { private fun createFunctionBlockDeclaration(name: String, type: DataType, x: Int, y: Int, assignments: List): FunctionBlockDeclaration { - val block = converterArguments.factory.createFunctionBlockDeclaration(null) + val block = factory.createFunctionBlockDeclaration(null) block.name = name block.typeReference.setTargetName(type.stringify()) block.x = x block.y = y val parameterAssignments = assignments.map { - val parameterAssign = converterArguments.factory.createParameterAssignment() - parameterAssign.value = STConverter.parseLiteral(converterArguments.stFactory, it) + val parameterAssign = factory.createParameterAssignment() + parameterAssign.value = STConverter.parseLiteral(stFactory, it) // parameterAssign.parameterReference.setTargetName(it.third) parameterAssign } diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/RootDeclarationPrinter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/RootDeclarationPrinter.kt index fde14210a..bda80f13a 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/RootDeclarationPrinter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/RootDeclarationPrinter.kt @@ -3,11 +3,12 @@ package org.fbme.lib.iec61499.stringify import org.fbme.lib.common.Declaration import org.fbme.lib.iec61499.declarations.* import org.fbme.lib.iec61499.parser.ConverterArguments +import org.fbme.lib.iec61499.parser.Iec61499ConverterConfiguration import org.jdom.DocType import org.jdom.Document import org.jdom.Element -class RootDeclarationPrinter(private val myDeclaration: Declaration, val converterArguments: ConverterArguments) { +class RootDeclarationPrinter(private val myDeclaration: Declaration, val converterArguments: Iec61499ConverterConfiguration) { fun print(): Document { val rootElement: Element = when (myDeclaration) { is AdapterTypeDeclaration -> AdapterTypePrinter(myDeclaration).print() diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/persistence/Iec61499ModelFactory.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/persistence/Iec61499ModelFactory.kt index bd307a0af..5a41129c8 100644 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/persistence/Iec61499ModelFactory.kt +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/persistence/Iec61499ModelFactory.kt @@ -24,6 +24,7 @@ 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.MpsLanguages +import org.fbme.ide.platform.converter.PlatformConverter import org.fbme.ide.platform.converter.PlatformConverter.create import org.fbme.lib.common.Declaration import org.fbme.lib.common.RootElement @@ -270,8 +271,11 @@ class Iec61499ModelFactory : ModelFactory, DataLocationAwareModelFactory { // Write nodes to xml files for (rootNode in model.rootNodes) { + val owner = PlatformElementsOwner() + val conf = PlatformConverter.STANDARD_CONFIG_FACTORY.createConfiguration(owner) + val declaration = platformRepository.getAdapter(rootNode, Declaration::class.java) - val document = RootDeclarationPrinter(declaration!!).print() + val document = RootDeclarationPrinter(declaration!!, conf).print() val folderName = rootNode.getProperty(SNodeUtil.property_BaseConcept_virtualPackage).orEmpty().replace(".", "/") val fileLocalName = rootNode.name + "." + getExtensionOfSource(rootNode, platformRepository) @@ -307,6 +311,7 @@ class Iec61499ModelFactory : ModelFactory, DataLocationAwareModelFactory { const val DEV_FILE_EXT = "dev" const val SEG_FILE_EXT = "seg" const val SYS_FILE_EXT = "sys" + const val CFG_FILE_EXT = "cfg" const val HEADER_FILE_EXT = "iec61499" const val HEADER_FILE = "header.iec61499" @@ -340,6 +345,7 @@ class Iec61499ModelFactory : ModelFactory, DataLocationAwareModelFactory { DEV_FILE_EXT -> (converter.convertDeviceType() as PlatformElement).node SEG_FILE_EXT -> (converter.convertSegmentType() as PlatformElement).node SYS_FILE_EXT -> (converter.convertSystemConfiguration() as PlatformElement).node + CFG_FILE_EXT -> (converter.convertCATConfiguration() as PlatformElement).node else -> null } } @@ -354,6 +360,7 @@ class Iec61499ModelFactory : ModelFactory, DataLocationAwareModelFactory { is DeviceTypeDeclaration -> DEV_FILE_EXT is SegmentTypeDeclaration -> SEG_FILE_EXT is SystemDeclaration -> SYS_FILE_EXT + is CATBlockTypeDeclaration -> SYS_FILE_EXT else -> null } } From dbb6393af298d05b818afbb22e173454eabe5501 Mon Sep 17 00:00:00 2001 From: Valentina Danilova Date: Sun, 14 May 2023 15:23:48 +0300 Subject: [PATCH 12/17] hmi and dependent generation --- .mps/encodings.xml | 4 - .../hmi-blocks/JSON_DESERIALIZER.fbt | 0 .../hmi-blocks/JSON_FORMER.fbt | 0 .../hmi-blocks/JSON_PARSER.fbt | 0 .../hmi-blocks/JSON_SERIALIZER.fbt | 0 .../hmi-blocks/TYPE_DETECTOR.fbt | 0 .../hmi-blocks/TYPE_SERIALIZER.fbt | 0 .../org.fbme.ide.iec61499.lang.editor.mps | 6 + ...bme.ide.iec61499.adapter.interfacepart.mps | 27 +++ .../declarations/CATBlockTypeDeclaration.kt | 2 +- .../iec61499/parser/CATBlockTypeConverter.kt | 2 +- .../iec61499/parser/HMIInterfaceConverter.kt | 2 +- .../fbme/lib/iec61499/parser/RootConverter.kt | 2 +- .../DependentDeclarationGenerator.kt | 2 +- .../lib/iec61499/stringify/HMIBlockPrinter.kt | 201 +++++++++++++++++- .../stringify/HMIInterfaceTypeGenerator.kt | 174 +++++++++------ .../persistence/Iec61499ModelFactory.kt | 23 +- 17 files changed, 366 insertions(+), 79 deletions(-) delete mode 100644 .mps/encodings.xml rename code/{library/src/main/resources => 4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib}/hmi-blocks/JSON_DESERIALIZER.fbt (100%) rename code/{library/src/main/resources => 4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib}/hmi-blocks/JSON_FORMER.fbt (100%) rename code/{library/src/main/resources => 4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib}/hmi-blocks/JSON_PARSER.fbt (100%) rename code/{library/src/main/resources => 4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib}/hmi-blocks/JSON_SERIALIZER.fbt (100%) rename code/{library/src/main/resources => 4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib}/hmi-blocks/TYPE_DETECTOR.fbt (100%) rename code/{library/src/main/resources => 4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib}/hmi-blocks/TYPE_SERIALIZER.fbt (100%) diff --git a/.mps/encodings.xml b/.mps/encodings.xml deleted file mode 100644 index 15a15b218..000000000 --- a/.mps/encodings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/code/library/src/main/resources/hmi-blocks/JSON_DESERIALIZER.fbt b/code/4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib/hmi-blocks/JSON_DESERIALIZER.fbt similarity index 100% rename from code/library/src/main/resources/hmi-blocks/JSON_DESERIALIZER.fbt rename to code/4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib/hmi-blocks/JSON_DESERIALIZER.fbt diff --git a/code/library/src/main/resources/hmi-blocks/JSON_FORMER.fbt b/code/4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib/hmi-blocks/JSON_FORMER.fbt similarity index 100% rename from code/library/src/main/resources/hmi-blocks/JSON_FORMER.fbt rename to code/4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib/hmi-blocks/JSON_FORMER.fbt diff --git a/code/library/src/main/resources/hmi-blocks/JSON_PARSER.fbt b/code/4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib/hmi-blocks/JSON_PARSER.fbt similarity index 100% rename from code/library/src/main/resources/hmi-blocks/JSON_PARSER.fbt rename to code/4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib/hmi-blocks/JSON_PARSER.fbt diff --git a/code/library/src/main/resources/hmi-blocks/JSON_SERIALIZER.fbt b/code/4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib/hmi-blocks/JSON_SERIALIZER.fbt similarity index 100% rename from code/library/src/main/resources/hmi-blocks/JSON_SERIALIZER.fbt rename to code/4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib/hmi-blocks/JSON_SERIALIZER.fbt diff --git a/code/library/src/main/resources/hmi-blocks/TYPE_DETECTOR.fbt b/code/4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib/hmi-blocks/TYPE_DETECTOR.fbt similarity index 100% rename from code/library/src/main/resources/hmi-blocks/TYPE_DETECTOR.fbt rename to code/4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib/hmi-blocks/TYPE_DETECTOR.fbt diff --git a/code/library/src/main/resources/hmi-blocks/TYPE_SERIALIZER.fbt b/code/4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib/hmi-blocks/TYPE_SERIALIZER.fbt similarity index 100% rename from code/library/src/main/resources/hmi-blocks/TYPE_SERIALIZER.fbt rename to code/4diac-integration/solutions/stdlib/models/iec61499.4diac.stdlib/hmi-blocks/TYPE_SERIALIZER.fbt diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps index 84cdad963..3d440c81f 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps @@ -4406,6 +4406,9 @@ + + + @@ -4415,6 +4418,9 @@ + + + diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps index a71448294..a50c13f3a 100644 --- a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps +++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps @@ -7740,6 +7740,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/CATBlockTypeDeclaration.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/CATBlockTypeDeclaration.kt index c9f27cd00..84632a1d4 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/CATBlockTypeDeclaration.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/CATBlockTypeDeclaration.kt @@ -8,5 +8,5 @@ import org.fbme.lib.common.Reference interface CATBlockTypeDeclaration : Declaration, RootElement { val hmiInterface: Reference val blockDeclaration: Reference - val interfaceFileName: String + var interfaceFileName: String } \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/CATBlockTypeConverter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/CATBlockTypeConverter.kt index 8a9e7525e..29a275664 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/CATBlockTypeConverter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/CATBlockTypeConverter.kt @@ -13,7 +13,7 @@ class CATBlockTypeConverter(arguments: ConverterArguments) : val fbtd = factory.createCATBlockTypeDeclaration(identifier) fbtd.blockDeclaration.setTargetName(element.getChild("Composite").getAttributeValue("Type")) fbtd.hmiInterface.setTargetName(element.getChild("HMI").getAttributeValue("Type")) - + fbtd.interfaceFileName = element.getChild("HMI").getAttributeValue("InterfaceFile") return fbtd } } \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/HMIInterfaceConverter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/HMIInterfaceConverter.kt index 879d7eead..08b3bcab6 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/HMIInterfaceConverter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/HMIInterfaceConverter.kt @@ -18,7 +18,7 @@ class HMIInterfaceConverter(arguments: ConverterArguments) : fbtd.inputParameters ) ParameterDeclarationConverter.extractAll( - with(interfaceListElement.getChild("InputVars")), + with(interfaceListElement.getChild("OutputVars")), fbtd.outputParameters ) diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/RootConverter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/RootConverter.kt index 3e4233d51..cbbff2411 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/RootConverter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/RootConverter.kt @@ -10,7 +10,7 @@ class RootConverter( ) { fun convertFBType(): FBTypeDeclaration { val root = myDocument.rootElement - if (root.getAttribute("UsedInCAT") != null && root.getAttribute("UsedInCAT").value == "TRUE") { + if (root.getAttribute("UsedInCAT") != null && root.getAttribute("UsedInCAT").value == "True") { return HMIInterfaceConverter(arguments()).extract() } if (root.getChild("FBNetwork") != null) { diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/DependentDeclarationGenerator.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/DependentDeclarationGenerator.kt index f9a795f8c..a63070b9f 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/DependentDeclarationGenerator.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/DependentDeclarationGenerator.kt @@ -14,7 +14,7 @@ class DependentDeclarationGenerator(private val myDeclaration: Declaration, priv fun generate(): List { val rootElements: List = when (myDeclaration) { is HMIInterfaceTypeDeclaration -> HMIInterfaceTypeGenerator(myDeclaration, converterArguments).generateDependents() - else -> error("Unrecognized root declaration") + else -> listOf() } diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIBlockPrinter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIBlockPrinter.kt index 25350ebbc..7b1efc3a4 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIBlockPrinter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIBlockPrinter.kt @@ -1,10 +1,15 @@ package org.fbme.lib.iec61499.stringify +import org.fbme.lib.iec61499.IEC61499Factory import org.fbme.lib.iec61499.declarations.CompositeFBTypeDeclaration +import org.fbme.lib.iec61499.declarations.EventDeclaration import org.fbme.lib.iec61499.declarations.HMIInterfaceTypeDeclaration -import org.fbme.lib.iec61499.parser.ConverterArguments +import org.fbme.lib.iec61499.declarations.ParameterDeclaration +import org.fbme.lib.iec61499.fbnetwork.EntryKind +import org.fbme.lib.iec61499.fbnetwork.FunctionBlockDeclaration import org.fbme.lib.iec61499.parser.Iec61499ConverterConfiguration import org.fbme.lib.iec61499.parser.STConverter +import org.fbme.lib.st.STFactory import org.fbme.lib.st.types.ElementaryType import org.jdom.Element @@ -17,16 +22,31 @@ class HMIBlockPrinter(declaration: HMIInterfaceTypeDeclaration, val converterArg element.addContent(FBInterfacePrinterWithAdapters(this.element).print()) val cFB = CompositeFBTypePrinter(generateComposite()).print().children for (c in cFB) { - element.addContent(c) + element.addContent(c.clone().detach()) } element.setAttribute("UsedInCAT", "True") } fun generateComposite(): CompositeFBTypeDeclaration { - var cFB = factory.createCompositeFBTypeDeclaration(null) + val cFB = factory.createCompositeFBTypeDeclaration(null) cFB.name = this.element.name - cFB.inputParameters.addAll(this.element.inputParameters) - cFB.outputParameters.addAll(this.element.outputParameters) + this.element.inputParameters.forEach { + if (it.name != "MAPPING") { + val pD = factory.createParameterDeclaration(null) + pD.name = it.name + pD.type = it.type + pD.initialValue = it.initialValue + cFB.inputParameters.add(pD) + } + } + this.element.outputParameters.forEach { + val pD = factory.createParameterDeclaration(null) + pD.name = it.name + pD.type = it.type + pD.initialValue = it.initialValue + cFB.outputParameters.add(pD) + } + val initEvent = factory.createEventDeclaration(null) initEvent.name = "INIT" initEvent.associations @@ -34,6 +54,177 @@ class HMIBlockPrinter(declaration: HMIInterfaceTypeDeclaration, val converterArg mappingInput.name = "MAPPING" mappingInput.type = ElementaryType.STRING cFB.inputEvents.add(initEvent) + + if (this.element.inputParameters.size - 1 > 0) { +// NEEDED FUNCTION BLOCKS GENERATION + + cFB.network.functionBlocks.add(generateCommunicationBlock("PUBLISH", factory, stFactory)) + cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("JSON_SERIALIZER", factory)) + cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("DISPATCH_OUT_${element.name}", factory)) + +// EVENTS GENERATION + + val cEInitPublish = factory.createFBNetworkConnection(EntryKind.EVENT) + cEInitPublish.sourceReference.setFQName("INIT") + cEInitPublish.targetReference.setFQName("PUBLISH_1.INIT") + cFB.network.eventConnections.add(cEInitPublish) + + val cESerializerPublish = factory.createFBNetworkConnection(EntryKind.EVENT) + cESerializerPublish.sourceReference.setFQName("JSON_SERIALIZER.RES") + cESerializerPublish.targetReference.setFQName("PUBLISH_1.REQ") + cFB.network.eventConnections.add(cESerializerPublish) + + this.element.inputParameters.stream().filter{it.name != "MAPPING"} + .forEach{ + val event = generateEventForParameter(it, factory) + cFB.inputEvents.add(event) + val cEDispatch = factory.createFBNetworkConnection(EntryKind.EVENT) + cEDispatch.sourceReference.setFQName(event.name) + cEDispatch.targetReference.setFQName("DISPATCH_OUT_${this.element.name}.${event.name}") + cFB.network.eventConnections.add(cEDispatch) + } + +// DATA CONNECTIONS GENERATION + + val cDSerializerPublish = factory.createFBNetworkConnection(EntryKind.DATA) + cDSerializerPublish.sourceReference.setFQName("JSON_SERIALIZER.MSG") + cDSerializerPublish.targetReference.setFQName("PUBLISH_1.SD_1") + cFB.network.dataConnections.add(cDSerializerPublish) + + this.element.inputParameters.stream() + .forEach{ + val cDDispatch = factory.createFBNetworkConnection(EntryKind.DATA) + cDDispatch.sourceReference.setFQName(it.name) + cDDispatch.targetReference.setFQName("DISPATCH_OUT_${this.element.name}.${it.name}") + cFB.network.dataConnections.add(cDDispatch) + } + + val cDNameSerialize = factory.createFBNetworkConnection(EntryKind.DATA) + cDNameSerialize.sourceReference.setFQName("DISPATCH_OUT_${this.element.name}.NAME") + cDNameSerialize.targetReference.setFQName("JSON_SERIALIZER.NAME") + cFB.network.dataConnections.add(cDNameSerialize) + +// CONNECTIONS FOR DIFFERENT DATA TYPES + + HMIInterfaceTypeGenerator.CONNECTION_TYPES.forEach { + val cEDispatch = factory.createFBNetworkConnection(EntryKind.EVENT) + cEDispatch.sourceReference.setFQName("DISPATCH_OUT_${this.element.name}.IS_${it.name}") + cEDispatch.targetReference.setFQName("JSON_SERIALIZER.IS_${it.name}") + cFB.network.eventConnections.add(cEDispatch) + val cDDispatch = factory.createFBNetworkConnection(EntryKind.DATA) + cDDispatch.sourceReference.setFQName("DISPATCH_OUT_${this.element.name}.${it.name}_VALUE") + cDDispatch.targetReference.setFQName("JSON_SERIALIZER.${it.name}_VALUE") + cFB.network.dataConnections.add(cDDispatch) + } + + } + + if (this.element.outputParameters.size > 0) { +// NEEDED FUNCTION BLOCKS GENERATION + + cFB.network.functionBlocks.add(generateCommunicationBlock("SUBSCRIBE", factory, stFactory)) + cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("JSON_DESERIALIZER", factory)) + cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("DISPATCH_IN_${element.name}", factory)) + +// EVENTS GENERATION + + val cEInitSubscribe = factory.createFBNetworkConnection(EntryKind.EVENT) + cEInitSubscribe.sourceReference.setFQName("INIT") + cEInitSubscribe.targetReference.setFQName("SUBSCRIBE_1.INIT") + cFB.network.eventConnections.add(cEInitSubscribe) + + val cESubscribeDeserialize = factory.createFBNetworkConnection(EntryKind.EVENT) + cESubscribeDeserialize.sourceReference.setFQName("SUBSCRIBE_1.IND") + cESubscribeDeserialize.targetReference.setFQName("JSON_DESERIALIZER.REQ") + cFB.network.eventConnections.add(cESubscribeDeserialize) + + val cEDeserializeDispatch = factory.createFBNetworkConnection(EntryKind.EVENT) + cEDeserializeDispatch.sourceReference.setFQName("JSON_DESERIALIZER.RES") + cEDeserializeDispatch.targetReference.setFQName("DISPATCH_IN_${this.element}.REQ") + cFB.network.eventConnections.add(cEDeserializeDispatch) + + this.element.outputParameters.forEach { + val event = generateEventForParameter(it, factory) + cFB.outputEvents.add(event) + + val cEDispatchRes = factory.createFBNetworkConnection(EntryKind.EVENT) + cEDispatchRes.sourceReference.setFQName("DISPATCH_IN_${this.element}.${event.name}") + cEDispatchRes.targetReference.setFQName(event.name) + cFB.network.eventConnections.add(cEDispatchRes) + } + +// DATA GENERATION + + val cDSubscribeDeserialize = factory.createFBNetworkConnection(EntryKind.DATA) + cDSubscribeDeserialize.sourceReference.setFQName("SUBSCRIBE_1.RD_1") + cDSubscribeDeserialize.targetReference.setFQName("JSON_DESERIALIZER.DATA") + cFB.network.dataConnections.add(cDSubscribeDeserialize) + + val cDDeserializeName = factory.createFBNetworkConnection(EntryKind.DATA) + cDDeserializeName.sourceReference.setFQName("JSON_DESERIALIZER.NAME") + cDDeserializeName.targetReference.setFQName("DISPATCH_IN_${this.element.name}.NAME") + cFB.network.dataConnections.add(cDDeserializeName) + + val cDMappingDispatch = factory.createFBNetworkConnection(EntryKind.DATA) + cDMappingDispatch.sourceReference.setFQName("MAPPING") + cDMappingDispatch.targetReference.setFQName("DISPATCH_IN_${this.element.name}.MAPPING") + cFB.network.dataConnections.add(cDMappingDispatch) + + this.element.outputParameters.forEach { + val cEDispatchRes = factory.createFBNetworkConnection(EntryKind.DATA) + cEDispatchRes.sourceReference.setFQName("DISPATCH_IN_${this.element.name}.${it.name}") + cEDispatchRes.targetReference.setFQName(it.name) + cFB.network.eventConnections.add(cEDispatchRes) + } + + HMIInterfaceTypeGenerator.CONNECTION_TYPES.forEach { + val cDDispatch = factory.createFBNetworkConnection(EntryKind.DATA) + cDDispatch.sourceReference.setFQName("JSON_DESERIALIZER.${it.name}_VALUE") + cDDispatch.targetReference.setFQName("DISPATCH_IN_${this.element.name}.${it.name}_VALUE") + cFB.network.dataConnections.add(cDDispatch) + } + } + + return cFB } + + fun generateEventForParameter(parameter: ParameterDeclaration, factory: IEC61499Factory): EventDeclaration { + val currEvent = factory.createEventDeclaration(null) + currEvent.name = "IS_${parameter.name}" + val assoc = factory.createEventAssociation() + assoc.parameterReference.setTarget(parameter) + currEvent.associations.add(assoc) + return currEvent + } + + fun generateCommunicationBlock(name: String, factory: IEC61499Factory, stFactory: STFactory): FunctionBlockDeclaration { + val fB = factory.createFunctionBlockDeclaration(null) + fB.name = name + val idPD = factory.createParameterDeclaration(null) + idPD.name = "ID" + idPD.type = ElementaryType.WSTRING + val host = factory.createParameterAssignment() + host.parameterReference.setTarget(idPD) + host.value = STConverter.parseLiteral(stFactory, "225.0.0.2:65011") + fB.parameters.add(host) + + val qiPD = factory.createParameterDeclaration(null) + qiPD.name = "QI" + qiPD.type = ElementaryType.BOOL + val qi = factory.createParameterAssignment() + qi.parameterReference.setTarget(idPD) + qi.value = STConverter.parseLiteral(stFactory, "1") + fB.parameters.add(qi) + + fB.typeReference.setTargetName(name) + return fB + } + + fun generateFunctionDeclarationNoParam(name: String, factory: IEC61499Factory): FunctionBlockDeclaration { + val fB = factory.createFunctionBlockDeclaration(null) + fB.name = name + fB.typeReference.setTargetName(name) + return fB + } } \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt index d2a5e75dc..8570a836a 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt @@ -1,13 +1,9 @@ package org.fbme.lib.iec61499.stringify -import org.fbme.lib.common.Declaration import org.fbme.lib.common.Identifier import org.fbme.lib.iec61499.IEC61499Factory import org.fbme.lib.iec61499.declarations.* -import org.fbme.lib.iec61499.fbnetwork.EntryKind import org.fbme.lib.iec61499.fbnetwork.FunctionBlockDeclaration -import org.fbme.lib.iec61499.parser.CompositeFBTypeConverter -import org.fbme.lib.iec61499.parser.ConverterArguments import org.fbme.lib.iec61499.parser.Iec61499ConverterConfiguration import org.fbme.lib.iec61499.parser.STConverter import org.fbme.lib.st.STFactory @@ -15,7 +11,6 @@ import org.fbme.lib.st.expressions.Expression import org.fbme.lib.st.statements.Statement import org.fbme.lib.st.types.DataType import org.fbme.lib.st.types.ElementaryType -import org.jdom.Element import java.util.function.BiFunction class HMIInterfaceTypeGenerator(val declaration: HMIInterfaceTypeDeclaration, val converterArguments: Iec61499ConverterConfiguration) { @@ -25,12 +20,13 @@ class HMIInterfaceTypeGenerator(val declaration: HMIInterfaceTypeDeclaration, va fun generateDependents(): List { val elements = mutableListOf() - val outFb = generateDispatchOut(factory, stFactory, declaration.outputParameters) + val outFb = generateDispatchOut(factory, stFactory, declaration.inputParameters, declaration.name) elements.add(outFb) val inFb = generateDispatchIn( factory, stFactory, - declaration.inputParameters + declaration.outputParameters, + declaration.name ) elements.add(inFb) // val hmiElement = Element(declaration.name) @@ -48,31 +44,69 @@ companion object { ElementaryType.STRING ) - fun generateDispatchOut(factory: IEC61499Factory, stFactory: STFactory, outputVars: List): FBTypeDeclaration { + fun generateDispatchOut(factory: IEC61499Factory, stFactory: STFactory, outputVars: List, name: String = ""): FBTypeDeclaration { val bfb = factory.createBasicFBTypeDeclaration(null) + bfb.name = "DISPATCH_OUT_${name}" - for (oV in outputVars) { + val startState = factory.createStateDeclaration(null) + startState.name = "START" + bfb.ecc.states.add(startState) + val nameDeclaration = generateParameterDeclaration(factory, "NAME", ElementaryType.STRING, listOf()) + val mappingDeclaration = generateParameterDeclaration(factory, "MAPPING", ElementaryType.STRING, listOf()) + bfb.inputParameters.add(mappingDeclaration) + bfb.outputParameters.add(nameDeclaration) + + val outConnections = generateTypedConnections(factory, listOf(), CONNECTION_TYPES) + bfb.outputParameters.addAll(outConnections) + val typedEventsMap = mutableMapOf() + outConnections.forEach { + val typedEvent = factory.createEventDeclaration(null) + typedEvent.name = "IS_${it.type}" + val assoc = factory.createEventAssociation() + assoc.parameterReference.setTarget(it) + typedEvent.associations.add(assoc) + + val nameAssoc = factory.createEventAssociation() + nameAssoc.parameterReference.setTarget(nameDeclaration) + typedEvent.associations.add(nameAssoc) + bfb.outputEvents.add(typedEvent) + typedEventsMap.put(it.type!!, typedEvent) } - for (oV in outputVars) { + + for (iV in outputVars) { val state = factory.createStateDeclaration(null) - state.name = "SET_${oV.name}" + state.name = "SET_${iV.name}" val action = factory.createStateAction() val event = factory.createEventDeclaration(null) - val eventAssociation = factory.createEventAssociation() - eventAssociation.parameterReference.setTarget(factory.createParameterDeclaration(null)) - event.name = "RES_${oV.type}" - event.associations.add(eventAssociation) -// action.event.setTarget(PortPath.createEventPortPath(bfb, event)) + event.name = "IS_${iV.name}" + val mappingAssoc = factory.createEventAssociation() + mappingAssoc.parameterReference.setTarget(mappingDeclaration) + event.associations.add(mappingAssoc) + val currParameter = generateParameterDeclaration(factory, iV.name, iV.type, listOf(event)) + currParameter.initialValue = iV.initialValue + bfb.inputParameters.add(currParameter) + bfb.inputEvents.add(event) val algorithm = factory.createAlgorithmDeclaration(null) - algorithm.name = "set${oV.name}" + algorithm.name = "set${iV.name}" val algorithmBody = factory.createAlgorithmBody(AlgorithmLanguage.ST) - val outCode = generateOutCode(oV, algorithm, factory, stFactory) + val outCode = generateOutCode(iV, algorithm, factory, stFactory) algorithmBody.statements.addAll(outCode) algorithm.body = algorithmBody - stFactory.createVariableReference() + action.algorithm.setTarget(algorithm) + bfb.algorithms.add(algorithm) state.actions.add(action) bfb.ecc.states.add(state) + + val fromStartTransition = factory.createStateTransition() + fromStartTransition.condition.eventReference.setFQName("IS_${iV.name}") + fromStartTransition.sourceReference.setTarget(startState) + fromStartTransition.targetReference.setTarget(state) + bfb.ecc.transitions.add(fromStartTransition) + val toStartTransition = factory.createStateTransition() + toStartTransition.sourceReference.setTarget(state) + toStartTransition.targetReference.setTarget(startState) + bfb.ecc.transitions.add(toStartTransition) } return bfb @@ -85,58 +119,88 @@ companion object { val startState = factory.createStateDeclaration(null) startState.name = "START" bfb.ecc.states.add(startState) - val inConnections = generateTypedConnections(factory, CONNECTION_TYPES) + val reqEvent = factory.createEventDeclaration(null) + reqEvent.name = "REQ" + bfb.inputEvents.add(reqEvent) + val inConnections = generateTypedConnections(factory, listOf(reqEvent), CONNECTION_TYPES) + val nameDeclaration = generateParameterDeclaration(factory, "NAME", ElementaryType.STRING, listOf(reqEvent)) + val mappingDeclaration = generateParameterDeclaration(factory, "MAPPING", ElementaryType.STRING, listOf(reqEvent)) bfb.inputParameters.addAll(inConnections) - for (oV in inputVars) { - val state = factory.createStateDeclaration(null) - state.name = "SET_${oV.name}" - val action = factory.createStateAction() + bfb.inputParameters.add(nameDeclaration) + bfb.inputParameters.add(mappingDeclaration) + for (iV in inputVars) { + if (iV.name == "MAPPING") { + continue + } + val event = factory.createEventDeclaration(null) val eventAssociation = factory.createEventAssociation() + + val state = factory.createStateDeclaration(null) + state.name = "SET_${iV.name}" + val action = factory.createStateAction() val currParameter = factory.createParameterDeclaration(null) - currParameter.name = oV.name - currParameter.type = oV.type - currParameter.initialValue = oV.initialValue + currParameter.name = iV.name + currParameter.type = iV.type + currParameter.initialValue = iV.initialValue bfb.outputParameters.add(currParameter) eventAssociation.parameterReference.setTarget(currParameter) - event.name = "RES_${oV.type}" + event.name = "IS_${iV.name}" event.associations.add(eventAssociation) -// action.event.setTarget(PortPath.createEventPortPath(bfb, event)) + bfb.outputEvents.add(event) val algorithm = factory.createAlgorithmDeclaration(null) - algorithm.name = "set${oV.name}" + algorithm.name = "set${iV.name}" val algorithmBody = factory.createAlgorithmBody(AlgorithmLanguage.ST) - val outCode = generateInCode(oV, algorithm, factory, stFactory) + val outCode = generateInCode(iV, algorithm, factory, stFactory) algorithmBody.statements.addAll(outCode) algorithm.body = algorithmBody action.algorithm.setTarget(algorithm) bfb.algorithms.add(algorithm) state.actions.add(action) bfb.ecc.states.add(state) + val fromStartTransition = factory.createStateTransition() + fromStartTransition.condition.setGuardCondition(generateInGuard(iV, stFactory)) + fromStartTransition.sourceReference.setTarget(startState) + fromStartTransition.targetReference.setTarget(state) + bfb.ecc.transitions.add(fromStartTransition) + val toStartTransition = factory.createStateTransition() + toStartTransition.sourceReference.setTarget(state) + toStartTransition.targetReference.setTarget(startState) + bfb.ecc.transitions.add(toStartTransition) } -// for (oV in inputVars) { -// val stateTransition = factory.createStateTransition() -// stateTransition.condition.setGuardCondition(generateInGuard(oV, stFactory)) -// bfb.ecc.transitions.add(stateTransition) -// } return bfb } - fun generateTypedConnections(factory: IEC61499Factory, types: List): List { + fun generateTypedConnections(factory: IEC61499Factory, events: List, types: List): List { val resTypes = mutableListOf() types.forEach { val p = factory.createParameterDeclaration(null) p.type = it p.name = "${p.type}_VALUE" resTypes.add(p) + events.forEach { + val assoc = factory.createEventAssociation() + assoc.parameterReference.setTarget(p) + it.associations.add(assoc) + } } - val p = factory.createParameterDeclaration(null) - p.type = ElementaryType.STRING - p.name = "NAME" - resTypes.add(p) return resTypes } + fun generateParameterDeclaration(factory: IEC61499Factory, name: String, type: DataType?, associatedEvents: List): ParameterDeclaration { + val p_ = factory.createParameterDeclaration(null) + p_.type = type + p_.name = name + associatedEvents.forEach { + val assoc = factory.createEventAssociation() + assoc.parameterReference.setTarget(p_) + it.associations.add(assoc) + } + return p_ + } + + fun generateCode(code: String, factory: IEC61499Factory, stFactory: STFactory, algorithmDeclaration: AlgorithmDeclaration): List { val parameterCollector = BiFunction { name: Identifier?, type: DataType? -> @@ -154,44 +218,26 @@ companion object { } fun generateOutCode(p: ParameterDeclaration, algorithmDeclaration: AlgorithmDeclaration, factory: IEC61499Factory, stFactory: STFactory): List { - var code = "VAR \n" + + val code = "VAR \n" + "TAG: STRING;\n" + "END_VAR;" + "TAG:= '#${p.name}';\n" + "NAME:=CONCAT(MAPPING, TAG);\n" + - "VALUE:=${p.name};]]>" - var statementList = generateCode(code, factory, stFactory, algorithmDeclaration) + "${p.type}_VALUE:=${p.name};]]>" + val statementList = generateCode(code, factory, stFactory, algorithmDeclaration) return statementList } fun generateInCode(p: ParameterDeclaration, algorithmDeclaration: AlgorithmDeclaration, factory: IEC61499Factory, stFactory: STFactory): List { - var code = "${p.name}:= ${p.type!!.stringify()}_VALUE;" + val code = "${p.name}:= ${p.type!!.stringify()}_VALUE;" return generateCode(code, factory, stFactory, algorithmDeclaration) } fun generateInGuard(p: ParameterDeclaration, stFactory: STFactory): Expression { - var code = "(LEFT(NAME, LEN(NAME) - 7) = MAPPING) AND (RIGHT(NAME, LEN(NAME) - LEN(NAME) + ${p.name.length + 1}) = '#${p.name}')" + val code = "(LEFT(NAME, LEN(NAME) - 7) = MAPPING) AND (RIGHT(NAME, LEN(NAME) - LEN(NAME) + ${p.name.length + 1}) = '#${p.name}')" return STConverter.parseExpression(stFactory, code)!! } } - - - private fun createFunctionBlockDeclaration(name: String, type: DataType, x: Int, y: Int, assignments: List): FunctionBlockDeclaration { - val block = factory.createFunctionBlockDeclaration(null) - block.name = name - block.typeReference.setTargetName(type.stringify()) - block.x = x - block.y = y - - val parameterAssignments = assignments.map { - val parameterAssign = factory.createParameterAssignment() - parameterAssign.value = STConverter.parseLiteral(stFactory, it) -// parameterAssign.parameterReference.setTargetName(it.third) - parameterAssign - } - block.parameters.addAll(parameterAssignments) - return block - } } \ No newline at end of file diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/persistence/Iec61499ModelFactory.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/persistence/Iec61499ModelFactory.kt index 5a41129c8..3a1c9395a 100644 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/persistence/Iec61499ModelFactory.kt +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/persistence/Iec61499ModelFactory.kt @@ -29,6 +29,7 @@ import org.fbme.ide.platform.converter.PlatformConverter.create import org.fbme.lib.common.Declaration import org.fbme.lib.common.RootElement import org.fbme.lib.iec61499.declarations.* +import org.fbme.lib.iec61499.stringify.DependentDeclarationGenerator import org.fbme.lib.iec61499.stringify.RootDeclarationPrinter import org.jdom.Document import org.jetbrains.mps.openapi.model.SModel @@ -46,6 +47,7 @@ import java.io.InputStreamReader import java.io.OutputStream import java.util.* import java.util.stream.Collectors +import kotlin.io.path.Path class Iec61499ModelFactory : ModelFactory, DataLocationAwareModelFactory { private fun supportedFileExtension(fileExt: String): Boolean { @@ -56,6 +58,7 @@ class Iec61499ModelFactory : ModelFactory, DataLocationAwareModelFactory { || fileExt == DEV_FILE_EXT || fileExt == SYS_FILE_EXT || fileExt == SEG_FILE_EXT + || fileExt == CFG_FILE_EXT } override fun supports(dataSource: DataSource): Boolean { @@ -269,6 +272,24 @@ class Iec61499ModelFactory : ModelFactory, DataLocationAwareModelFactory { .getComponent(MPSCoreComponents::class.java).moduleRepository val platformRepository = PlatformRepository(repository) + val dependents = mutableListOf() + for (rootNode in model.rootNodes) { + val owner = PlatformElementsOwner() + val conf = PlatformConverter.STANDARD_CONFIG_FACTORY.createConfiguration(owner) + + val declaration = platformRepository.getAdapter(rootNode, Declaration::class.java) + + val els = DependentDeclarationGenerator(declaration, conf).generate() + els.forEach { + val node = (it as PlatformElement).node + node.setProperty(SNodeUtil.property_BaseConcept_virtualPackage, rootNode.getProperty(SNodeUtil.property_BaseConcept_virtualPackage).orEmpty()) + dependents.add(node) + } + } + + LOG.info("Dependents size: {}", dependents.size) + dependents.forEach { model.addRootNode(it) } + // Write nodes to xml files for (rootNode in model.rootNodes) { val owner = PlatformElementsOwner() @@ -360,7 +381,7 @@ class Iec61499ModelFactory : ModelFactory, DataLocationAwareModelFactory { is DeviceTypeDeclaration -> DEV_FILE_EXT is SegmentTypeDeclaration -> SEG_FILE_EXT is SystemDeclaration -> SYS_FILE_EXT - is CATBlockTypeDeclaration -> SYS_FILE_EXT + is CATBlockTypeDeclaration -> CFG_FILE_EXT else -> null } } From 6d1dddd2f4300da8a2862dc7dd66f7bbb305ddef Mon Sep 17 00:00:00 2001 From: Valentina Danilova Date: Wed, 17 May 2023 14:58:49 +0300 Subject: [PATCH 13/17] generators --- .mps/modules.xml | 1 + .../org.fbme.ide.integration.fordiac.mps | 13 + .../org.fbme.ide.iec61499.lang.actions.mps | 3 + .../org.fbme.ide.iec61499.lang.editor.mps | 21 +- .../org.fbme.ide.iec61499.lang.intentions.mps | 268 ++++++++++++++++++ .../org.fbme.ide.iec61499.lang.mpl | 4 + .../repository/PlatformElementsOwner.kt | 2 +- .../HMIInterfaceTypeDeclaration.kt | 2 +- .../lib/iec61499/stringify/HMIBlockPrinter.kt | 221 +-------------- .../stringify/HMIInterfaceTypeGenerator.kt | 202 ++++++++++++- .../persistence/Iec61499ModelFactory.kt | 39 ++- .../src/test/kotlin/HmiPrinterTest.kt | 36 --- solutions/WaterTank/WaterTank.msd | 30 ++ .../WaterTank/models/WaterTank.water_tank.mps | 18 ++ 14 files changed, 595 insertions(+), 265 deletions(-) create mode 100644 code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.intentions.mps delete mode 100644 code/platform/src/test/kotlin/HmiPrinterTest.kt create mode 100644 solutions/WaterTank/WaterTank.msd create mode 100644 solutions/WaterTank/models/WaterTank.water_tank.mps diff --git a/.mps/modules.xml b/.mps/modules.xml index 13a61f80f..9ca59999e 100644 --- a/.mps/modules.xml +++ b/.mps/modules.xml @@ -33,6 +33,7 @@ + \ No newline at end of file diff --git a/code/4diac-integration/solutions/org.fbme.ide.integration.fordiac/models/org.fbme.ide.integration.fordiac.mps b/code/4diac-integration/solutions/org.fbme.ide.integration.fordiac/models/org.fbme.ide.integration.fordiac.mps index d79377fa0..4c8fe5821 100644 --- a/code/4diac-integration/solutions/org.fbme.ide.integration.fordiac/models/org.fbme.ide.integration.fordiac.mps +++ b/code/4diac-integration/solutions/org.fbme.ide.integration.fordiac/models/org.fbme.ide.integration.fordiac.mps @@ -474,6 +474,7 @@ + @@ -487,6 +488,7 @@ + @@ -3052,6 +3054,17 @@ + + + + + + + + + + + diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.actions.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.actions.mps index 957e003e7..299199ef6 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.actions.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.actions.mps @@ -114,5 +114,8 @@ + + + diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps index 3d440c81f..537cf030b 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps @@ -170,6 +170,7 @@ + @@ -4413,7 +4414,10 @@ - + + + + @@ -4433,12 +4437,21 @@ - - - + + + + + + + + + + + + diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.intentions.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.intentions.mps new file mode 100644 index 000000000..e6a4884b9 --- /dev/null +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.intentions.mps @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/org.fbme.ide.iec61499.lang.mpl b/code/language/languages/org.fbme.ide.iec61499.lang/org.fbme.ide.iec61499.lang.mpl index 9304586c9..5e19666f5 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/org.fbme.ide.iec61499.lang.mpl +++ b/code/language/languages/org.fbme.ide.iec61499.lang/org.fbme.ide.iec61499.lang.mpl @@ -73,6 +73,7 @@ 1db6de07-b355-4c0f-9979-75b4ac1e8215(org.fbme.lib) ce018f97-56b9-4ee7-9b5f-2d462b6628bf(org.fbme.platform.lib) d7eb0a2a-bd50-4576-beae-e4a89db35f20(jetbrains.mps.lang.scopes.runtime) + 1ed103c3-3aa6-49b7-9c21-6765ee11f224(MPS.Editor) @@ -125,7 +126,10 @@ + + + diff --git a/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformElementsOwner.kt b/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformElementsOwner.kt index 4dd6c3a65..12ad891fb 100644 --- a/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformElementsOwner.kt +++ b/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformElementsOwner.kt @@ -15,7 +15,7 @@ open class PlatformElementsOwner { if (adapter == null) { elements.remove(node) } - return requiredClass.cast(adapter)!! + return requiredClass.cast(adapter) } private fun adapt(node: SNode): Element? { diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/HMIInterfaceTypeDeclaration.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/HMIInterfaceTypeDeclaration.kt index e9bfcdb47..8a4908a4f 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/HMIInterfaceTypeDeclaration.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/HMIInterfaceTypeDeclaration.kt @@ -1,5 +1,5 @@ package org.fbme.lib.iec61499.declarations -interface HMIInterfaceTypeDeclaration : FBTypeDeclaration { +interface HMIInterfaceTypeDeclaration : FBTypeDeclaration{ } \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIBlockPrinter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIBlockPrinter.kt index 7b1efc3a4..7eaea2f42 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIBlockPrinter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIBlockPrinter.kt @@ -1,230 +1,23 @@ package org.fbme.lib.iec61499.stringify -import org.fbme.lib.iec61499.IEC61499Factory -import org.fbme.lib.iec61499.declarations.CompositeFBTypeDeclaration -import org.fbme.lib.iec61499.declarations.EventDeclaration import org.fbme.lib.iec61499.declarations.HMIInterfaceTypeDeclaration -import org.fbme.lib.iec61499.declarations.ParameterDeclaration -import org.fbme.lib.iec61499.fbnetwork.EntryKind -import org.fbme.lib.iec61499.fbnetwork.FunctionBlockDeclaration import org.fbme.lib.iec61499.parser.Iec61499ConverterConfiguration -import org.fbme.lib.iec61499.parser.STConverter -import org.fbme.lib.st.STFactory -import org.fbme.lib.st.types.ElementaryType import org.jdom.Element class HMIBlockPrinter(declaration: HMIInterfaceTypeDeclaration, val converterArguments: Iec61499ConverterConfiguration) : - DeclarationPrinterBase(declaration, "FBType") { + DeclarationPrinterBase(declaration, "HMIDeclaration") { val factory = converterArguments.entryFactory val stFactory = converterArguments.stEntryFactory override fun printDeclarationBody(element: Element) { element.addContent(FBInterfacePrinterWithAdapters(this.element).print()) - val cFB = CompositeFBTypePrinter(generateComposite()).print().children - for (c in cFB) { - element.addContent(c.clone().detach()) - } +// val cFB = CompositeFBTypePrinter(HMIInterfaceTypeGenerator.generateComposite(factory, stFactory, this.element)).print().children +// for (c in cFB) { +// element.addContent(c.clone().detach()) +// } + addNullableContent(element, ParameterDeclarationPrinter.printAll("InputVars", this.element.inputParameters)) + addNullableContent(element, ParameterDeclarationPrinter.printAll("OutputVars", this.element.outputParameters)) element.setAttribute("UsedInCAT", "True") } - fun generateComposite(): CompositeFBTypeDeclaration { - val cFB = factory.createCompositeFBTypeDeclaration(null) - cFB.name = this.element.name - this.element.inputParameters.forEach { - if (it.name != "MAPPING") { - val pD = factory.createParameterDeclaration(null) - pD.name = it.name - pD.type = it.type - pD.initialValue = it.initialValue - cFB.inputParameters.add(pD) - } - } - this.element.outputParameters.forEach { - val pD = factory.createParameterDeclaration(null) - pD.name = it.name - pD.type = it.type - pD.initialValue = it.initialValue - cFB.outputParameters.add(pD) - } - - val initEvent = factory.createEventDeclaration(null) - initEvent.name = "INIT" - initEvent.associations - val mappingInput = factory.createParameterDeclaration(null) - mappingInput.name = "MAPPING" - mappingInput.type = ElementaryType.STRING - cFB.inputEvents.add(initEvent) - - if (this.element.inputParameters.size - 1 > 0) { -// NEEDED FUNCTION BLOCKS GENERATION - - cFB.network.functionBlocks.add(generateCommunicationBlock("PUBLISH", factory, stFactory)) - cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("JSON_SERIALIZER", factory)) - cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("DISPATCH_OUT_${element.name}", factory)) - -// EVENTS GENERATION - - val cEInitPublish = factory.createFBNetworkConnection(EntryKind.EVENT) - cEInitPublish.sourceReference.setFQName("INIT") - cEInitPublish.targetReference.setFQName("PUBLISH_1.INIT") - cFB.network.eventConnections.add(cEInitPublish) - - val cESerializerPublish = factory.createFBNetworkConnection(EntryKind.EVENT) - cESerializerPublish.sourceReference.setFQName("JSON_SERIALIZER.RES") - cESerializerPublish.targetReference.setFQName("PUBLISH_1.REQ") - cFB.network.eventConnections.add(cESerializerPublish) - - this.element.inputParameters.stream().filter{it.name != "MAPPING"} - .forEach{ - val event = generateEventForParameter(it, factory) - cFB.inputEvents.add(event) - val cEDispatch = factory.createFBNetworkConnection(EntryKind.EVENT) - cEDispatch.sourceReference.setFQName(event.name) - cEDispatch.targetReference.setFQName("DISPATCH_OUT_${this.element.name}.${event.name}") - cFB.network.eventConnections.add(cEDispatch) - } - -// DATA CONNECTIONS GENERATION - - val cDSerializerPublish = factory.createFBNetworkConnection(EntryKind.DATA) - cDSerializerPublish.sourceReference.setFQName("JSON_SERIALIZER.MSG") - cDSerializerPublish.targetReference.setFQName("PUBLISH_1.SD_1") - cFB.network.dataConnections.add(cDSerializerPublish) - - this.element.inputParameters.stream() - .forEach{ - val cDDispatch = factory.createFBNetworkConnection(EntryKind.DATA) - cDDispatch.sourceReference.setFQName(it.name) - cDDispatch.targetReference.setFQName("DISPATCH_OUT_${this.element.name}.${it.name}") - cFB.network.dataConnections.add(cDDispatch) - } - - val cDNameSerialize = factory.createFBNetworkConnection(EntryKind.DATA) - cDNameSerialize.sourceReference.setFQName("DISPATCH_OUT_${this.element.name}.NAME") - cDNameSerialize.targetReference.setFQName("JSON_SERIALIZER.NAME") - cFB.network.dataConnections.add(cDNameSerialize) - -// CONNECTIONS FOR DIFFERENT DATA TYPES - - HMIInterfaceTypeGenerator.CONNECTION_TYPES.forEach { - val cEDispatch = factory.createFBNetworkConnection(EntryKind.EVENT) - cEDispatch.sourceReference.setFQName("DISPATCH_OUT_${this.element.name}.IS_${it.name}") - cEDispatch.targetReference.setFQName("JSON_SERIALIZER.IS_${it.name}") - cFB.network.eventConnections.add(cEDispatch) - val cDDispatch = factory.createFBNetworkConnection(EntryKind.DATA) - cDDispatch.sourceReference.setFQName("DISPATCH_OUT_${this.element.name}.${it.name}_VALUE") - cDDispatch.targetReference.setFQName("JSON_SERIALIZER.${it.name}_VALUE") - cFB.network.dataConnections.add(cDDispatch) - } - - } - - if (this.element.outputParameters.size > 0) { -// NEEDED FUNCTION BLOCKS GENERATION - - cFB.network.functionBlocks.add(generateCommunicationBlock("SUBSCRIBE", factory, stFactory)) - cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("JSON_DESERIALIZER", factory)) - cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("DISPATCH_IN_${element.name}", factory)) - -// EVENTS GENERATION - - val cEInitSubscribe = factory.createFBNetworkConnection(EntryKind.EVENT) - cEInitSubscribe.sourceReference.setFQName("INIT") - cEInitSubscribe.targetReference.setFQName("SUBSCRIBE_1.INIT") - cFB.network.eventConnections.add(cEInitSubscribe) - - val cESubscribeDeserialize = factory.createFBNetworkConnection(EntryKind.EVENT) - cESubscribeDeserialize.sourceReference.setFQName("SUBSCRIBE_1.IND") - cESubscribeDeserialize.targetReference.setFQName("JSON_DESERIALIZER.REQ") - cFB.network.eventConnections.add(cESubscribeDeserialize) - - val cEDeserializeDispatch = factory.createFBNetworkConnection(EntryKind.EVENT) - cEDeserializeDispatch.sourceReference.setFQName("JSON_DESERIALIZER.RES") - cEDeserializeDispatch.targetReference.setFQName("DISPATCH_IN_${this.element}.REQ") - cFB.network.eventConnections.add(cEDeserializeDispatch) - - this.element.outputParameters.forEach { - val event = generateEventForParameter(it, factory) - cFB.outputEvents.add(event) - - val cEDispatchRes = factory.createFBNetworkConnection(EntryKind.EVENT) - cEDispatchRes.sourceReference.setFQName("DISPATCH_IN_${this.element}.${event.name}") - cEDispatchRes.targetReference.setFQName(event.name) - cFB.network.eventConnections.add(cEDispatchRes) - } - -// DATA GENERATION - - val cDSubscribeDeserialize = factory.createFBNetworkConnection(EntryKind.DATA) - cDSubscribeDeserialize.sourceReference.setFQName("SUBSCRIBE_1.RD_1") - cDSubscribeDeserialize.targetReference.setFQName("JSON_DESERIALIZER.DATA") - cFB.network.dataConnections.add(cDSubscribeDeserialize) - - val cDDeserializeName = factory.createFBNetworkConnection(EntryKind.DATA) - cDDeserializeName.sourceReference.setFQName("JSON_DESERIALIZER.NAME") - cDDeserializeName.targetReference.setFQName("DISPATCH_IN_${this.element.name}.NAME") - cFB.network.dataConnections.add(cDDeserializeName) - - val cDMappingDispatch = factory.createFBNetworkConnection(EntryKind.DATA) - cDMappingDispatch.sourceReference.setFQName("MAPPING") - cDMappingDispatch.targetReference.setFQName("DISPATCH_IN_${this.element.name}.MAPPING") - cFB.network.dataConnections.add(cDMappingDispatch) - - this.element.outputParameters.forEach { - val cEDispatchRes = factory.createFBNetworkConnection(EntryKind.DATA) - cEDispatchRes.sourceReference.setFQName("DISPATCH_IN_${this.element.name}.${it.name}") - cEDispatchRes.targetReference.setFQName(it.name) - cFB.network.eventConnections.add(cEDispatchRes) - } - - HMIInterfaceTypeGenerator.CONNECTION_TYPES.forEach { - val cDDispatch = factory.createFBNetworkConnection(EntryKind.DATA) - cDDispatch.sourceReference.setFQName("JSON_DESERIALIZER.${it.name}_VALUE") - cDDispatch.targetReference.setFQName("DISPATCH_IN_${this.element.name}.${it.name}_VALUE") - cFB.network.dataConnections.add(cDDispatch) - } - } - - - return cFB - } - - fun generateEventForParameter(parameter: ParameterDeclaration, factory: IEC61499Factory): EventDeclaration { - val currEvent = factory.createEventDeclaration(null) - currEvent.name = "IS_${parameter.name}" - val assoc = factory.createEventAssociation() - assoc.parameterReference.setTarget(parameter) - currEvent.associations.add(assoc) - return currEvent - } - - fun generateCommunicationBlock(name: String, factory: IEC61499Factory, stFactory: STFactory): FunctionBlockDeclaration { - val fB = factory.createFunctionBlockDeclaration(null) - fB.name = name - val idPD = factory.createParameterDeclaration(null) - idPD.name = "ID" - idPD.type = ElementaryType.WSTRING - val host = factory.createParameterAssignment() - host.parameterReference.setTarget(idPD) - host.value = STConverter.parseLiteral(stFactory, "225.0.0.2:65011") - fB.parameters.add(host) - - val qiPD = factory.createParameterDeclaration(null) - qiPD.name = "QI" - qiPD.type = ElementaryType.BOOL - val qi = factory.createParameterAssignment() - qi.parameterReference.setTarget(idPD) - qi.value = STConverter.parseLiteral(stFactory, "1") - fB.parameters.add(qi) - - fB.typeReference.setTargetName(name) - return fB - } - - fun generateFunctionDeclarationNoParam(name: String, factory: IEC61499Factory): FunctionBlockDeclaration { - val fB = factory.createFunctionBlockDeclaration(null) - fB.name = name - fB.typeReference.setTargetName(name) - return fB - } } \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt index 8570a836a..be081f12a 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt @@ -3,6 +3,7 @@ package org.fbme.lib.iec61499.stringify import org.fbme.lib.common.Identifier import org.fbme.lib.iec61499.IEC61499Factory import org.fbme.lib.iec61499.declarations.* +import org.fbme.lib.iec61499.fbnetwork.EntryKind import org.fbme.lib.iec61499.fbnetwork.FunctionBlockDeclaration import org.fbme.lib.iec61499.parser.Iec61499ConverterConfiguration import org.fbme.lib.iec61499.parser.STConverter @@ -29,8 +30,8 @@ class HMIInterfaceTypeGenerator(val declaration: HMIInterfaceTypeDeclaration, va declaration.name ) elements.add(inFb) -// val hmiElement = Element(declaration.name) -// printComposite(hmiElement, outFb, inFb) + val compositeHMI = generateComposite(factory, stFactory, declaration) + elements.add(compositeHMI) return elements } @@ -238,6 +239,203 @@ companion object { val code = "(LEFT(NAME, LEN(NAME) - 7) = MAPPING) AND (RIGHT(NAME, LEN(NAME) - LEN(NAME) + ${p.name.length + 1}) = '#${p.name}')" return STConverter.parseExpression(stFactory, code)!! } + + fun generateComposite(factory: IEC61499Factory, stFactory: STFactory, declaration: HMIInterfaceTypeDeclaration): CompositeFBTypeDeclaration { + var cFB = factory.createCompositeFBTypeDeclaration(null) + cFB.name = declaration.name + "_HMI_COMPOSITE" + declaration.inputParameters.forEach { + if (it.name != "MAPPING") { + val pD = factory.createParameterDeclaration(null) + pD.name = it.name + pD.type = it.type + pD.initialValue = it.initialValue + cFB.inputParameters.add(pD) + } + } + declaration.outputParameters.forEach { + val pD = factory.createParameterDeclaration(null) + pD.name = it.name + pD.type = it.type + pD.initialValue = it.initialValue + cFB.outputParameters.add(pD) + } + + val initEvent = factory.createEventDeclaration(null) + initEvent.name = "INIT" + initEvent.associations + val mappingInput = factory.createParameterDeclaration(null) + mappingInput.name = "MAPPING" + mappingInput.type = ElementaryType.STRING + cFB.inputParameters.add(mappingInput) + cFB.inputEvents.add(initEvent) + + if (cFB.inputParameters.size - 1 > 0) { +// NEEDED FUNCTION BLOCKS GENERATION + + cFB.network.functionBlocks.add(generateCommunicationBlock("PUBLISH_1", factory, stFactory)) + cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("JSON_SERIALIZER", factory)) + cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("DISPATCH_OUT_${declaration.name}", factory)) + +// EVENTS GENERATION + + val cEInitPublish = factory.createFBNetworkConnection(EntryKind.EVENT) + cEInitPublish.sourceReference.setFQName("INIT") + cEInitPublish.targetReference.setFQName("PUBLISH_1.INIT") + cFB.network.eventConnections.add(cEInitPublish) + + val cESerializerPublish = factory.createFBNetworkConnection(EntryKind.EVENT) + cESerializerPublish.sourceReference.setFQName("JSON_SERIALIZER.RES") + cESerializerPublish.targetReference.setFQName("PUBLISH_1.REQ") + cFB.network.eventConnections.add(cESerializerPublish) + + declaration.inputParameters.stream().filter{it.name != "MAPPING"} + .forEach{ + val event = generateEventForParameter(it, factory) + cFB.inputEvents.add(event) + val cEDispatch = factory.createFBNetworkConnection(EntryKind.EVENT) + cEDispatch.sourceReference.setFQName(event.name) + cEDispatch.targetReference.setFQName("DISPATCH_OUT_${declaration.name}.${event.name}") + cFB.network.eventConnections.add(cEDispatch) + } + +// DATA CONNECTIONS GENERATION + + val cDSerializerPublish = factory.createFBNetworkConnection(EntryKind.DATA) + cDSerializerPublish.sourceReference.setFQName("JSON_SERIALIZER.MSG") + cDSerializerPublish.targetReference.setFQName("PUBLISH_1.SD_1") + cFB.network.dataConnections.add(cDSerializerPublish) + + declaration.inputParameters.stream() + .forEach{ + val cDDispatch = factory.createFBNetworkConnection(EntryKind.DATA) + cDDispatch.sourceReference.setFQName(it.name) + cDDispatch.targetReference.setFQName("DISPATCH_OUT_${declaration.name}.${it.name}") + cFB.network.dataConnections.add(cDDispatch) + } + + val cDNameSerialize = factory.createFBNetworkConnection(EntryKind.DATA) + cDNameSerialize.sourceReference.setFQName("DISPATCH_OUT_${declaration.name}.NAME") + cDNameSerialize.targetReference.setFQName("JSON_SERIALIZER.NAME") + cFB.network.dataConnections.add(cDNameSerialize) + +// CONNECTIONS FOR DIFFERENT DATA TYPES + + HMIInterfaceTypeGenerator.CONNECTION_TYPES.forEach { + val cEDispatch = factory.createFBNetworkConnection(EntryKind.EVENT) + cEDispatch.sourceReference.setFQName("DISPATCH_OUT_${declaration.name}.IS_${it.name}") + cEDispatch.targetReference.setFQName("JSON_SERIALIZER.IS_${it.name}") + cFB.network.eventConnections.add(cEDispatch) + val cDDispatch = factory.createFBNetworkConnection(EntryKind.DATA) + cDDispatch.sourceReference.setFQName("DISPATCH_OUT_${declaration.name}.${it.name}_VALUE") + cDDispatch.targetReference.setFQName("JSON_SERIALIZER.${it.name}_VALUE") + cFB.network.dataConnections.add(cDDispatch) + } + + } + + if (declaration.outputParameters.size > 0) { +// NEEDED FUNCTION BLOCKS GENERATION + + cFB.network.functionBlocks.add(generateCommunicationBlock("SUBSCRIBE_1", factory, stFactory, 65012)) + cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("JSON_DESERIALIZER", factory)) + cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("DISPATCH_IN_${declaration.name}", factory)) + +// EVENTS GENERATION + + val cEInitSubscribe = factory.createFBNetworkConnection(EntryKind.EVENT) + cEInitSubscribe.sourceReference.setFQName("INIT") + cEInitSubscribe.targetReference.setFQName("SUBSCRIBE_1.INIT") + cFB.network.eventConnections.add(cEInitSubscribe) + + val cESubscribeDeserialize = factory.createFBNetworkConnection(EntryKind.EVENT) + cESubscribeDeserialize.sourceReference.setFQName("SUBSCRIBE_1.IND") + cESubscribeDeserialize.targetReference.setFQName("JSON_DESERIALIZER.REQ") + cFB.network.eventConnections.add(cESubscribeDeserialize) + + val cEDeserializeDispatch = factory.createFBNetworkConnection(EntryKind.EVENT) + cEDeserializeDispatch.sourceReference.setFQName("JSON_DESERIALIZER.RES") + cEDeserializeDispatch.targetReference.setFQName("DISPATCH_IN_${declaration.name}.REQ") + cFB.network.eventConnections.add(cEDeserializeDispatch) + + declaration.outputParameters.forEach { + val event = generateEventForParameter(it, factory) + cFB.outputEvents.add(event) + + val cEDispatchRes = factory.createFBNetworkConnection(EntryKind.EVENT) + cEDispatchRes.sourceReference.setFQName("DISPATCH_IN_${declaration.name}.${event.name}") + cEDispatchRes.targetReference.setFQName(event.name) + cFB.network.eventConnections.add(cEDispatchRes) + } + +// DATA GENERATION + + val cDSubscribeDeserialize = factory.createFBNetworkConnection(EntryKind.DATA) + cDSubscribeDeserialize.sourceReference.setFQName("SUBSCRIBE_1.RD_1") + cDSubscribeDeserialize.targetReference.setFQName("JSON_DESERIALIZER.DATA") + cFB.network.dataConnections.add(cDSubscribeDeserialize) + + val cDDeserializeName = factory.createFBNetworkConnection(EntryKind.DATA) + cDDeserializeName.sourceReference.setFQName("JSON_DESERIALIZER.NAME") + cDDeserializeName.targetReference.setFQName("DISPATCH_IN_${declaration.name}.NAME") + cFB.network.dataConnections.add(cDDeserializeName) + + val cDMappingDispatch = factory.createFBNetworkConnection(EntryKind.DATA) + cDMappingDispatch.sourceReference.setFQName("MAPPING") + cDMappingDispatch.targetReference.setFQName("DISPATCH_IN_${declaration.name}.MAPPING") + cFB.network.dataConnections.add(cDMappingDispatch) + + declaration.outputParameters.forEach { + val cEDispatchRes = factory.createFBNetworkConnection(EntryKind.DATA) + cEDispatchRes.sourceReference.setFQName("DISPATCH_IN_${declaration.name}.${it.name}") + cEDispatchRes.targetReference.setFQName(it.name) + cFB.network.dataConnections.add(cEDispatchRes) + } + + HMIInterfaceTypeGenerator.CONNECTION_TYPES.forEach { + val cDDispatch = factory.createFBNetworkConnection(EntryKind.DATA) + cDDispatch.sourceReference.setFQName("JSON_DESERIALIZER.${it.name}_VALUE") + cDDispatch.targetReference.setFQName("DISPATCH_IN_${declaration.name}.${it.name}_VALUE") + cFB.network.dataConnections.add(cDDispatch) + } + } + + + return cFB + } + + fun generateEventForParameter(parameter: ParameterDeclaration, factory: IEC61499Factory): EventDeclaration { + val currEvent = factory.createEventDeclaration(null) + currEvent.name = "IS_${parameter.name}" + val assoc = factory.createEventAssociation() + assoc.parameterReference.setTarget(parameter) + currEvent.associations.add(assoc) + return currEvent + } + + fun generateCommunicationBlock(name: String, factory: IEC61499Factory, stFactory: STFactory, port: Int = 65011): FunctionBlockDeclaration { + val fB = factory.createFunctionBlockDeclaration(null) + fB.name = name + val host = factory.createParameterAssignment() + host.parameterReference.setTargetName("ID") + host.value = STConverter.parseLiteral(stFactory, "\"225.0.0.2:${port}\"") + fB.parameters.add(host) + + + val qi = factory.createParameterAssignment() + qi.parameterReference.setTargetName("QI") + qi.value = STConverter.parseLiteral(stFactory, "1") + fB.parameters.add(qi) + + fB.typeReference.setTargetName(name) + return fB + } + + fun generateFunctionDeclarationNoParam(name: String, factory: IEC61499Factory): FunctionBlockDeclaration { + val fB = factory.createFunctionBlockDeclaration(null) + fB.name = name + fB.typeReference.setTargetName(name) + return fB + } } } \ No newline at end of file diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/persistence/Iec61499ModelFactory.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/persistence/Iec61499ModelFactory.kt index 3a1c9395a..14c0a0e65 100644 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/persistence/Iec61499ModelFactory.kt +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/persistence/Iec61499ModelFactory.kt @@ -15,6 +15,12 @@ import jetbrains.mps.persistence.DataLocationAwareModelFactory import jetbrains.mps.smodel.SModel.ImportElement import jetbrains.mps.smodel.SModelId import jetbrains.mps.smodel.SNodeUtil +import jetbrains.mps.smodel.adapter.ids.SContainmentLinkId +import jetbrains.mps.smodel.adapter.ids.SPropertyId +import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory +import jetbrains.mps.smodel.adapter.structure.link.SContainmentLinkAdapter +import jetbrains.mps.smodel.adapter.structure.link.SContainmentLinkAdapterById +import jetbrains.mps.smodel.adapter.structure.property.SPropertyAdapter import jetbrains.mps.util.FileUtil import jetbrains.mps.util.JDOMUtil import jetbrains.mps.util.NameUtil @@ -29,6 +35,8 @@ import org.fbme.ide.platform.converter.PlatformConverter.create import org.fbme.lib.common.Declaration import org.fbme.lib.common.RootElement import org.fbme.lib.iec61499.declarations.* +import org.fbme.lib.iec61499.parser.Iec61499ConverterConfiguration +import org.fbme.lib.iec61499.parser.StandardIec61499ConverterConfiguration import org.fbme.lib.iec61499.stringify.DependentDeclarationGenerator import org.fbme.lib.iec61499.stringify.RootDeclarationPrinter import org.jdom.Document @@ -272,28 +280,43 @@ class Iec61499ModelFactory : ModelFactory, DataLocationAwareModelFactory { .getComponent(MPSCoreComponents::class.java).moduleRepository val platformRepository = PlatformRepository(repository) + val nodesToDelete = mutableListOf() val dependents = mutableListOf() for (rootNode in model.rootNodes) { + if (rootNode.getProperty(SNodeUtil.property_BaseConcept_virtualPackage).orEmpty().endsWith("_dependent")) { +// c.dropReference(it.link) + nodesToDelete.add(rootNode) + continue + } val owner = PlatformElementsOwner() val conf = PlatformConverter.STANDARD_CONFIG_FACTORY.createConfiguration(owner) - val declaration = platformRepository.getAdapter(rootNode, Declaration::class.java) val els = DependentDeclarationGenerator(declaration, conf).generate() - els.forEach { - val node = (it as PlatformElement).node - node.setProperty(SNodeUtil.property_BaseConcept_virtualPackage, rootNode.getProperty(SNodeUtil.property_BaseConcept_virtualPackage).orEmpty()) + els.forEach { element -> + val node = (element as PlatformElement).node + node.setProperty(SNodeUtil.property_BaseConcept_virtualPackage, rootNode.getProperty(SNodeUtil.property_BaseConcept_virtualPackage).orEmpty() + ".${rootNode.name}_dependent") dependents.add(node) } } + LOG.info("Deleting nodes size: {}", nodesToDelete.size) + nodesToDelete.forEach { + model.enterUpdateMode() + model.removeRootNode(it) + it.delete() + model.leaveUpdateMode() + } LOG.info("Dependents size: {}", dependents.size) - dependents.forEach { model.addRootNode(it) } + dependents.forEach { + model.enterUpdateMode() + model.addRootNode(it) + model.leaveUpdateMode() + } // Write nodes to xml files for (rootNode in model.rootNodes) { - val owner = PlatformElementsOwner() - val conf = PlatformConverter.STANDARD_CONFIG_FACTORY.createConfiguration(owner) + val conf = StandardIec61499ConverterConfiguration(platformRepository.iec61499Factory, platformRepository.stFactory) val declaration = platformRepository.getAdapter(rootNode, Declaration::class.java) val document = RootDeclarationPrinter(declaration!!, conf).print() @@ -322,6 +345,8 @@ class Iec61499ModelFactory : ModelFactory, DataLocationAwareModelFactory { } } + + companion object { private val LOG = LoggerFactory.getLogger(Iec61499ModelFactory::class.java) diff --git a/code/platform/src/test/kotlin/HmiPrinterTest.kt b/code/platform/src/test/kotlin/HmiPrinterTest.kt deleted file mode 100644 index 759a91da3..000000000 --- a/code/platform/src/test/kotlin/HmiPrinterTest.kt +++ /dev/null @@ -1,36 +0,0 @@ -import org.fbme.ide.platform.testing.PlatformTestBase -import org.fbme.ide.platform.testing.PlatformTestRunner -import org.fbme.lib.iec61499.parser.STConverter -import org.fbme.lib.iec61499.stringify.HMIInterfaceTypeGenerator.Companion.generateDispatchIn -import org.fbme.lib.st.types.ElementaryType -import org.jdom.Document -import org.jdom.Element -import org.jdom.output.XMLOutputter -import org.junit.Test -import org.junit.runner.RunWith -import java.io.FileOutputStream -import java.io.OutputStream - - -@RunWith(PlatformTestRunner::class) -class HMIPrinterTest : PlatformTestBase() { - @Test - fun parseTest1() { - print("Started") - var fileName = "DISPATCH_IN.fbt" - val resElement: Element = Element("DISPATCH_IN") - var p = factory.createParameterDeclaration(null) - p.type = ElementaryType.STRING - p.name = "COUNT" - p.initialValue = STConverter.parseLiteral(stFactory, "'1'") - -// generateDispatchIn(resElement, factory, stFactory, listOf(p)) -// resElement.print(PrettyPrinter()) - // List dependentList = new DependentDeclarationPrinter(declaration).print(); - val stream: OutputStream = FileOutputStream(fileName) - val xmlOutputter = XMLOutputter() - var doc = Document() - doc.setRootElement(resElement) - xmlOutputter.output(doc, stream) - } -} \ No newline at end of file diff --git a/solutions/WaterTank/WaterTank.msd b/solutions/WaterTank/WaterTank.msd new file mode 100644 index 000000000..c098be686 --- /dev/null +++ b/solutions/WaterTank/WaterTank.msd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + b8a7e14f-52ea-4ee2-b17e-26c27da8084c(IEC-61499) + 5aff85f5-c1e8-49b6-a1f1-66d79702cceb(org.fbme.ide.iec61499.adapter) + + + + + + + + + + + + + + diff --git a/solutions/WaterTank/models/WaterTank.water_tank.mps b/solutions/WaterTank/models/WaterTank.water_tank.mps new file mode 100644 index 000000000..5b459894f --- /dev/null +++ b/solutions/WaterTank/models/WaterTank.water_tank.mps @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + From 49c46c67501757fa3aeca5c2455731a9ccc88f84 Mon Sep 17 00:00:00 2001 From: Valentina Danilova Date: Wed, 17 May 2023 16:09:51 +0300 Subject: [PATCH 14/17] check inputs and outputs --- .../stringify/HMIInterfaceTypeGenerator.kt | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt index be081f12a..3f723ce25 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt @@ -21,15 +21,19 @@ class HMIInterfaceTypeGenerator(val declaration: HMIInterfaceTypeDeclaration, va fun generateDependents(): List { val elements = mutableListOf() - val outFb = generateDispatchOut(factory, stFactory, declaration.inputParameters, declaration.name) - elements.add(outFb) - val inFb = generateDispatchIn( - factory, - stFactory, - declaration.outputParameters, - declaration.name - ) - elements.add(inFb) + if (declaration.inputParameters.size > 0) { + val outFb = generateDispatchOut(factory, stFactory, declaration.inputParameters, declaration.name) + elements.add(outFb) + } + if (declaration.outputParameters.size > 0) { + val inFb = generateDispatchIn( + factory, + stFactory, + declaration.outputParameters, + declaration.name + ) + elements.add(inFb) + } val compositeHMI = generateComposite(factory, stFactory, declaration) elements.add(compositeHMI) return elements @@ -236,7 +240,7 @@ companion object { } fun generateInGuard(p: ParameterDeclaration, stFactory: STFactory): Expression { - val code = "(LEFT(NAME, LEN(NAME) - 7) = MAPPING) AND (RIGHT(NAME, LEN(NAME) - LEN(NAME) + ${p.name.length + 1}) = '#${p.name}')" + val code = "(LEFT(NAME, LEN(NAME) - ${p.name.length + 2}) = MAPPING) AND (RIGHT(NAME, LEN(NAME) - LEN(NAME) + ${p.name.length + 1}) = '#${p.name}')" return STConverter.parseExpression(stFactory, code)!! } From 57e867b3a0ae15f05ceb510680076aa07096b026 Mon Sep 17 00:00:00 2001 From: Valentina Danilova Date: Thu, 18 May 2023 10:42:38 +0300 Subject: [PATCH 15/17] . --- .../stringify/HMIInterfaceTypeGenerator.kt | 2 +- .../WaterTank/models/WaterTank.water_tank.mps | 27 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt index 3f723ce25..17ca8c5c2 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt @@ -246,7 +246,7 @@ companion object { fun generateComposite(factory: IEC61499Factory, stFactory: STFactory, declaration: HMIInterfaceTypeDeclaration): CompositeFBTypeDeclaration { var cFB = factory.createCompositeFBTypeDeclaration(null) - cFB.name = declaration.name + "_HMI_COMPOSITE" + cFB.name = declaration.name + "_HMI" declaration.inputParameters.forEach { if (it.name != "MAPPING") { val pD = factory.createParameterDeclaration(null) diff --git a/solutions/WaterTank/models/WaterTank.water_tank.mps b/solutions/WaterTank/models/WaterTank.water_tank.mps index 5b459894f..29796e363 100644 --- a/solutions/WaterTank/models/WaterTank.water_tank.mps +++ b/solutions/WaterTank/models/WaterTank.water_tank.mps @@ -10,9 +10,32 @@ - + + + + + + + + + + + + - + + + + + + + + + + + + + From ffb4d2ed99a48a45e456c71045790d7ad9b604bd Mon Sep 17 00:00:00 2001 From: Pechenka2005 Date: Fri, 2 Jun 2023 17:43:55 +0300 Subject: [PATCH 16/17] Create visual wditor with HMI interact --- .mps/modules.xml | 2 - .teamcity/buildTypes/FbmeBuildType.kt | 8 +- README.md | 2 + build-bootstrap.xml | 31 +- buildSrc/build.gradle.kts | 2 +- .../org/fbme/gradle/MpsBuildscriptTasks.kt | 4 +- buildscripts/models/org.fbme.ide.build.mps | 83 +- buildscripts/org.fbme.ide.build.msd | 1 - .../org.fbme.ide.integration.fordiac.mps | 118 +- code/cat_visual/README.md | 14 + code/cat_visual/build.gradle.kts | 49 + code/cat_visual/gradle.properties | 1 + code/cat_visual/gradlew | 185 + code/cat_visual/gradlew.bat | 89 + code/cat_visual/settings.gradle.kts | 10 + code/cat_visual/src/main/kotlin/Main.kt | 79 + .../src/main/kotlin/canvas/Canvas.kt | 16 + .../src/main/kotlin/canvas/UpperPlate.kt | 91 + .../main/kotlin/canvas/items/CustomItem.kt | 182 + .../connection/ConnectionFieldRegistry.kt | 45 + .../src/main/kotlin/connection/clients.kt | 174 + .../main/kotlin/connection/field/BoolField.kt | 37 + .../connection/field/ConnectionField.kt | 61 + .../kotlin/connection/field/DoubleField.kt | 36 + .../kotlin/connection/field/FloatField.kt | 33 + .../main/kotlin/connection/field/IntField.kt | 238 + .../kotlin/connection/field/StringField.kt | 31 + .../kotlin/connection/field/WStringField.kt | 31 + .../connection/provider/ConnectionProvider.kt | 89 + .../kotlin/example/COUNTER/COMMON_CONF.xml | 9 + .../src/main/kotlin/example/COUNTER/COUNT.xml | 0 .../main/kotlin/example/COUNTER/COUNTER.kt | 44 + .../main/kotlin/example/COUNTER/COUNTER.xml | 10 + .../kotlin/example/COUNTER/COUNTER_CONF.xml | 10 + .../kotlin/example/COUNTER/COUNTER_PLAIN.xml | 10 + .../src/main/kotlin/example/COUNTER/LAMP.xml | 10 + .../main/kotlin/example/COUNTER/TOGGLE.xml | 0 .../main/kotlin/example/INCHOICE/INCHOICE.kt | 75 + .../example/INCHOICE/INCHOICE_HMI.cnv.xml | 17 + .../kotlin/example/WATER_TANK/WATER_TANK.kt | 121 + .../kotlin/example/WATER_TANK/WATER_TANK.xml | 16 + .../example/WATER_TANK/WATER_TANK_CONF.xml | 10 + .../example/WATER_TANK/WATER_TANK_PLAIN.xml | 16 + .../kotlin/lib/elements/figures/elements.kt | 212 + .../kotlin/lib/elements/getters/elements.kt | 221 + .../kotlin/lib/elements/setters/elements.kt | 549 + .../src/main/kotlin/lib/visual/Positioning.kt | 32 + .../src/main/kotlin/lib/visual/colors.kt | 51 + .../main/kotlin/serializer/serialization.kt | 155 + .../cat_visual/src/test/kotlin/MockBackend.kt | 13 + .../src/test/kotlin/WaterTankTest.kt | 90 + code/debugger/build.gradle.kts | 9 +- .../models/org.fbme.debugger.build.mps | 300 - .../buildsolution/org.fbme.debugger.build.msd | 36 - .../models/org.fbme.debugger.plugin.mps | 9374 ----------------- .../org.fbme.debugger/org.fbme.debugger.msd | 66 - .../fbme/debugger/RuntimeTraceSynchronizer.kt | 140 +- .../main/kotlin/org/fbme/debugger/Utils.kt | 26 + .../org/fbme/debugger/common/value/Value.kt | 1 + .../debugger/plugin/DebuggerToolWindow.kt | 23 + .../plugin/DebuggerToolWindowFactory.kt | 11 + .../plugin/DeploymentAction_DeployResource.kt | 111 + .../plugin/DeploymentAction_StopResource.kt | 58 + .../InspectionsManager_ProjectPluginPart.kt | 19 + .../MonitoringAction_DisableWatching.kt | 33 + .../plugin/MonitoringAction_EnableWatching.kt | 39 + .../MonitoringAction_UnwatchECCState.kt | 32 + .../plugin/MonitoringAction_UnwatchFB.kt | 36 + .../plugin/MonitoringAction_UnwatchPort.kt | 36 + .../MonitoringAction_UnwatchSelectedFBs.kt | 32 + .../plugin/MonitoringAction_WatchECCState.kt | 32 + .../plugin/MonitoringAction_WatchFB.kt | 35 + .../plugin/MonitoringAction_WatchPort.kt | 36 + .../MonitoringAction_WatchSelectedFBs.kt | 33 + .../debugger/plugin/NetworkWatchActions.kt | 292 + .../plugin/SimulateExecutionAction.kt | 102 + .../src/main/resources/META-INF/plugin.xml | 59 +- .../org.fbme.ide.iec61499.lang.structure.mps | 1 - .../models/org.fbme.ide.st.lang.behavior.mps | 25 +- .../models/org.fbme.ide.st.lang.editor.mps | 121 +- .../models/org.fbme.ide.st.lang.structure.mps | 27 +- .../org.fbme.ide.iec61499.adapter.common.mps | 51 +- .../org.fbme.ide.iec61499.adapter.ecc.mps | 27 +- ...rg.fbme.ide.iec61499.adapter.fbnetwork.mps | 185 +- ....ide.iec61499.adapter.fbnetwork.subapp.mps | 40 +- ...bme.ide.iec61499.adapter.interfacepart.mps | 178 +- .../org.fbme.ide.iec61499.adapter.st.mps | 101 +- .../org.fbme.ide.iec61499.repository.mps | 23 + .../repository/PlatformDeclarationsScope.kt | 43 +- .../repository/PlatformElementsOwner.kt | 22 +- code/library/build.gradle.kts | 1 - .../main/antlr/org/fbme/lib/st/parser/ST.g4 | 39 +- .../ResourceFunctionBlockHierarchy.kt | 4 +- .../iec61499/parser/BasicFBTypeConverter.kt | 9 +- .../fbme/lib/iec61499/parser/ConverterBase.kt | 5 +- .../parser/ParameterAssignmentsConverter.kt | 5 +- .../fbme/lib/iec61499/parser/STConverter.kt | 19 +- .../stringify/ParameterAssignmentPrinter.kt | 5 +- .../fbme/lib/iec61499/stringify/STPrinter.kt | 126 +- .../fbme/lib/st/expressions/LiteralKind.kt | 2 +- code/nxt-integration/build.gradle.kts | 5 + .../nxt/importer/BasicFbTypeNxtImporter.kt | 8 +- .../nxt/importer/NxtProjectTemplate.kt | 97 +- .../importer/BasicFbTypeNxtImporterTest.kt | 30 + code/platform/build.gradle.kts | 4 +- .../models/org.fbme.ide.platform.mps | 150 - .../org.fbme.ide.platform.msd | 73 - .../ide/platform/debugger/DeviceConnection.kt | 18 +- .../ide/platform/debugger/WatchablePath.kt | 4 +- .../platform/debugger/WatchedValueListener.kt | 2 +- .../ide/platform/debugger/WatcherFacade.kt | 3 +- .../ide/platform/editor/HeaderedNodeEditor.kt | 5 +- .../platform/editor/ProjectionChooserPanel.kt | 2 +- .../platform/editor/SimpleEditorProjection.kt | 1 + .../persistence/PersistenceReferencesFixer.kt | 4 +- .../projectWizard/Iec61499ProjectTemplate.kt | 12 +- .../platform/projectWizard/LibraryTemplate.kt | 3 +- .../platform/projectWizard/SystemTemplate.kt | 3 +- .../platform/testing/PlatformTestRunner.kt | 2 +- .../org.fbme.ide.richediting.plugin.mps | 686 -- .../AllAlgorithmBodyVisibilityAction.kt | 9 +- .../actions/AllStateActionVisibilityAction.kt | 9 +- .../ide/richediting/actions/EditorActions.kt | 2 + .../adapters/fbnetwork/FBNetworkEditors.kt | 6 +- .../editor/RichResourceProjection.kt | 11 +- .../RichResourceProjectionController.kt | 2 +- .../plugin/InspectionsManagerRegistrar.kt | 18 + .../plugin/NetworkObservationKeys.kt | 25 + .../src/main/resources/META-INF/plugin.xml | 1 + code/scenes/build.gradle.kts | 1 - code/smv-debugger/build.gradle.kts | 2 + docs/css/video_embedding.css | 15 + docs/index.md | 5 + ....fbme.ide.iec61499.lang.sandbox.blinky.mps | 90 +- settings.gradle.kts | 29 +- 135 files changed, 5391 insertions(+), 11389 deletions(-) create mode 100644 code/cat_visual/README.md create mode 100644 code/cat_visual/build.gradle.kts create mode 100644 code/cat_visual/gradle.properties create mode 100755 code/cat_visual/gradlew create mode 100644 code/cat_visual/gradlew.bat create mode 100644 code/cat_visual/settings.gradle.kts create mode 100644 code/cat_visual/src/main/kotlin/Main.kt create mode 100644 code/cat_visual/src/main/kotlin/canvas/Canvas.kt create mode 100644 code/cat_visual/src/main/kotlin/canvas/UpperPlate.kt create mode 100644 code/cat_visual/src/main/kotlin/canvas/items/CustomItem.kt create mode 100644 code/cat_visual/src/main/kotlin/connection/ConnectionFieldRegistry.kt create mode 100644 code/cat_visual/src/main/kotlin/connection/clients.kt create mode 100644 code/cat_visual/src/main/kotlin/connection/field/BoolField.kt create mode 100644 code/cat_visual/src/main/kotlin/connection/field/ConnectionField.kt create mode 100644 code/cat_visual/src/main/kotlin/connection/field/DoubleField.kt create mode 100644 code/cat_visual/src/main/kotlin/connection/field/FloatField.kt create mode 100644 code/cat_visual/src/main/kotlin/connection/field/IntField.kt create mode 100644 code/cat_visual/src/main/kotlin/connection/field/StringField.kt create mode 100644 code/cat_visual/src/main/kotlin/connection/field/WStringField.kt create mode 100644 code/cat_visual/src/main/kotlin/connection/provider/ConnectionProvider.kt create mode 100644 code/cat_visual/src/main/kotlin/example/COUNTER/COMMON_CONF.xml create mode 100644 code/cat_visual/src/main/kotlin/example/COUNTER/COUNT.xml create mode 100644 code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER.kt create mode 100644 code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER.xml create mode 100644 code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER_CONF.xml create mode 100644 code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER_PLAIN.xml create mode 100644 code/cat_visual/src/main/kotlin/example/COUNTER/LAMP.xml create mode 100644 code/cat_visual/src/main/kotlin/example/COUNTER/TOGGLE.xml create mode 100644 code/cat_visual/src/main/kotlin/example/INCHOICE/INCHOICE.kt create mode 100644 code/cat_visual/src/main/kotlin/example/INCHOICE/INCHOICE_HMI.cnv.xml create mode 100644 code/cat_visual/src/main/kotlin/example/WATER_TANK/WATER_TANK.kt create mode 100644 code/cat_visual/src/main/kotlin/example/WATER_TANK/WATER_TANK.xml create mode 100644 code/cat_visual/src/main/kotlin/example/WATER_TANK/WATER_TANK_CONF.xml create mode 100644 code/cat_visual/src/main/kotlin/example/WATER_TANK/WATER_TANK_PLAIN.xml create mode 100644 code/cat_visual/src/main/kotlin/lib/elements/figures/elements.kt create mode 100644 code/cat_visual/src/main/kotlin/lib/elements/getters/elements.kt create mode 100644 code/cat_visual/src/main/kotlin/lib/elements/setters/elements.kt create mode 100644 code/cat_visual/src/main/kotlin/lib/visual/Positioning.kt create mode 100644 code/cat_visual/src/main/kotlin/lib/visual/colors.kt create mode 100644 code/cat_visual/src/main/kotlin/serializer/serialization.kt create mode 100644 code/cat_visual/src/test/kotlin/MockBackend.kt create mode 100644 code/cat_visual/src/test/kotlin/WaterTankTest.kt delete mode 100644 code/debugger/buildsolution/models/org.fbme.debugger.build.mps delete mode 100644 code/debugger/buildsolution/org.fbme.debugger.build.msd delete mode 100644 code/debugger/solutions/org.fbme.debugger/models/org.fbme.debugger.plugin.mps delete mode 100644 code/debugger/solutions/org.fbme.debugger/org.fbme.debugger.msd create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/Utils.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/DebuggerToolWindow.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/DebuggerToolWindowFactory.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/DeploymentAction_DeployResource.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/DeploymentAction_StopResource.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/InspectionsManager_ProjectPluginPart.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_DisableWatching.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_EnableWatching.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_UnwatchECCState.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_UnwatchFB.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_UnwatchPort.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_UnwatchSelectedFBs.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_WatchECCState.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_WatchFB.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_WatchPort.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_WatchSelectedFBs.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/NetworkWatchActions.kt create mode 100644 code/debugger/src/main/kotlin/org/fbme/debugger/plugin/SimulateExecutionAction.kt create mode 100644 code/nxt-integration/src/test/kotlin/org/fbme/integration/nxt/importer/BasicFbTypeNxtImporterTest.kt delete mode 100644 code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.mps delete mode 100644 code/platform/solutions/org.fbme.ide.platform/org.fbme.ide.platform.msd create mode 100644 code/richediting/src/main/kotlin/org/fbme/ide/richediting/plugin/InspectionsManagerRegistrar.kt create mode 100644 code/richediting/src/main/kotlin/org/fbme/ide/richediting/plugin/NetworkObservationKeys.kt create mode 100644 docs/css/video_embedding.css diff --git a/.mps/modules.xml b/.mps/modules.xml index 9ca59999e..a08ecb86d 100644 --- a/.mps/modules.xml +++ b/.mps/modules.xml @@ -7,8 +7,6 @@ - - diff --git a/.teamcity/buildTypes/FbmeBuildType.kt b/.teamcity/buildTypes/FbmeBuildType.kt index 98dd3691c..9592257ad 100644 --- a/.teamcity/buildTypes/FbmeBuildType.kt +++ b/.teamcity/buildTypes/FbmeBuildType.kt @@ -1,6 +1,7 @@ package buildTypes import jetbrains.buildServer.configs.kotlin.AbsoluteId +import jetbrains.buildServer.configs.kotlin.ArtifactRule import jetbrains.buildServer.configs.kotlin.BuildType import jetbrains.buildServer.configs.kotlin.DslContext import jetbrains.buildServer.configs.kotlin.buildSteps.gradle @@ -48,8 +49,11 @@ open class FbmeBuildType( runWithJbr() dependencies { artifacts(mpsArtifactsId) { - buildRule = tag("2021.3.1") - artifactRules = "MPS-213.7172.958.zip!/MPS 2021.3=>lib/MPS 2021.3" + buildRule = tag("2021.3.3") + rules = listOf( + ArtifactRule.include("MPS-213.*.zip!/MPS 2021.3", "lib/MPS 2021.3" ), + ArtifactRule.exclude("MPS-213.*-*.zip") + ) } } } diff --git a/README.md b/README.md index 71008c30e..6918c61e1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![JetBrains team project](https://jb.gg/badges/team.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) + # FBME **FBME** (Function Blocks Modelling Environment) is an IDE for IEC 61499 standard built on top of diff --git a/build-bootstrap.xml b/build-bootstrap.xml index e8bc609bf..4f552809e 100644 --- a/build-bootstrap.xml +++ b/build-bootstrap.xml @@ -10,14 +10,12 @@ - - @@ -34,7 +32,6 @@ - @@ -53,7 +50,7 @@ - mps.build.number=${mps.build.number}${line.separator}mps.date=${mps.date}${line.separator}mps.build.vcs.number=${mps.build.vcs.number}${line.separator}mps.teamcity.buildConfName=${mps.teamcity.buildConfName}${line.separator}mps.idea.platform.build.number=${mps.idea.platform.build.number}${line.separator}mps.mps.build.counter=${mps.mps.build.counter}${line.separator}mpsBootstrapCore.version.major=${mpsBootstrapCore.version.major}${line.separator}mpsBootstrapCore.version.minor=${mpsBootstrapCore.version.minor}${line.separator}mpsBootstrapCore.version.bugfixNr=${mpsBootstrapCore.version.bugfixNr}${line.separator}mpsBootstrapCore.version.eap=${mpsBootstrapCore.version.eap}${line.separator}mpsBootstrapCore.version=${mpsBootstrapCore.version}${line.separator}fbme_language.ide-version=${fbme_language.ide-version}${line.separator}fbme_richediting.ide-version=${fbme_richediting.ide-version}${line.separator}fbme_fordiac.ide-version=${fbme_fordiac.ide-version}${line.separator}fbme_smvdebugger.ide-version=${fbme_smvdebugger.ide-version}${line.separator}fbme_debugger.ide-version=${fbme_debugger.ide-version}${line.separator}fbme_nxt.ide-version=${fbme_nxt.ide-version} + mps.build.number=${mps.build.number}${line.separator}mps.date=${mps.date}${line.separator}mps.build.vcs.number=${mps.build.vcs.number}${line.separator}mps.teamcity.buildConfName=${mps.teamcity.buildConfName}${line.separator}mps.idea.platform.build.number=${mps.idea.platform.build.number}${line.separator}mps.mps.build.counter=${mps.mps.build.counter}${line.separator}mpsBootstrapCore.version.major=${mpsBootstrapCore.version.major}${line.separator}mpsBootstrapCore.version.minor=${mpsBootstrapCore.version.minor}${line.separator}mpsBootstrapCore.version.bugfixNr=${mpsBootstrapCore.version.bugfixNr}${line.separator}mpsBootstrapCore.version.eap=${mpsBootstrapCore.version.eap}${line.separator}mpsBootstrapCore.version=${mpsBootstrapCore.version}${line.separator}fbme_language.ide-version=${fbme_language.ide-version}${line.separator}fbme_richediting.ide-version=${fbme_richediting.ide-version}${line.separator}fbme_fordiac.ide-version=${fbme_fordiac.ide-version}${line.separator}fbme_smvdebugger.ide-version=${fbme_smvdebugger.ide-version}${line.separator}fbme_nxt.ide-version=${fbme_nxt.ide-version} @@ -62,7 +59,6 @@ - @@ -166,7 +162,6 @@ - @@ -207,10 +202,6 @@ - - - - @@ -229,7 +220,7 @@ - + @@ -238,7 +229,6 @@ - @@ -278,22 +268,6 @@ - - - - - - - - - - - - - - - - @@ -356,7 +330,6 @@ - diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 96f17bced..c86c2905e 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -7,7 +7,7 @@ plugins { } val mpsMajor = "2021.3" -val mpsMinor = "1" +val mpsMinor = "3" repositories { jcenter() diff --git a/buildSrc/src/main/kotlin/org/fbme/gradle/MpsBuildscriptTasks.kt b/buildSrc/src/main/kotlin/org/fbme/gradle/MpsBuildscriptTasks.kt index 3c78b4816..5796cfc66 100644 --- a/buildSrc/src/main/kotlin/org/fbme/gradle/MpsBuildscriptTasks.kt +++ b/buildSrc/src/main/kotlin/org/fbme/gradle/MpsBuildscriptTasks.kt @@ -26,9 +26,9 @@ fun MpsBuildscriptTasks(project: Project, mpsExtension: MpsExtension) { val mpsBinaries by configurations.creating val antBinaries by configurations.creating - val implementation by configurations.getting + val compileOnly by configurations.getting val mpsImplementation by configurations.getting { - extendsFrom(implementation, mpsBinaries) + extendsFrom(compileOnly, mpsBinaries) } val mpsInput by configurations.registering { diff --git a/buildscripts/models/org.fbme.ide.build.mps b/buildscripts/models/org.fbme.ide.build.mps index a95ea2012..a64ec79a1 100644 --- a/buildscripts/models/org.fbme.ide.build.mps +++ b/buildscripts/models/org.fbme.ide.build.mps @@ -15,7 +15,6 @@ - @@ -190,6 +189,9 @@ + + + @@ -1121,9 +1123,16 @@ - - - + + + + + + + + + + @@ -1243,69 +1252,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1575,9 +1521,6 @@ - - - diff --git a/buildscripts/org.fbme.ide.build.msd b/buildscripts/org.fbme.ide.build.msd index 00eff04d2..eaedf74a0 100644 --- a/buildscripts/org.fbme.ide.build.msd +++ b/buildscripts/org.fbme.ide.build.msd @@ -18,7 +18,6 @@ 1e230472-5031-41e3-a8db-02d0c36f3ee2(org.fbme.samples.statisticsPlugin.build) 73b76635-8a63-49a1-ae16-da85b8524b49(org.fbme.smvDebugger.build) ab2b933c-4cca-47c1-aeca-d2ac0bca990d(org.fbme.language.build) - ff08ccef-06a9-4eaf-af2e-092d35667607(org.fbme.debugger.build) 9ffd2636-8012-4730-a841-d808550cbdea(org.fbme.integration.nxt.build) d76643a8-b8a1-4b1a-9500-66247bf6bc35(org.fbme.ide.build.plugins) diff --git a/code/4diac-integration/solutions/org.fbme.ide.integration.fordiac/models/org.fbme.ide.integration.fordiac.mps b/code/4diac-integration/solutions/org.fbme.ide.integration.fordiac/models/org.fbme.ide.integration.fordiac.mps index 4c8fe5821..85d87ef57 100644 --- a/code/4diac-integration/solutions/org.fbme.ide.integration.fordiac/models/org.fbme.ide.integration.fordiac.mps +++ b/code/4diac-integration/solutions/org.fbme.ide.integration.fordiac/models/org.fbme.ide.integration.fordiac.mps @@ -60,10 +60,10 @@ + - @@ -1576,7 +1576,7 @@ - + @@ -2377,8 +2377,8 @@ - - + + @@ -2402,8 +2402,8 @@ - - + + @@ -2422,6 +2422,9 @@ + + + @@ -2433,9 +2436,6 @@ - - - @@ -2975,7 +2975,7 @@ - + @@ -3190,7 +3190,7 @@ - + @@ -3208,8 +3208,8 @@ - - + + @@ -3219,66 +3219,46 @@ - - - - - - + + + + + + - + - - - - - - - - - - - + + + + + + + + + + + - - + + - + - - - - - - - - - - - - - - - - - - - - - + @@ -3288,7 +3268,7 @@ - + @@ -3334,7 +3314,7 @@ - + @@ -3357,7 +3337,7 @@ - + @@ -3380,7 +3360,7 @@ - + @@ -3412,8 +3392,8 @@ - - + + @@ -3448,8 +3428,8 @@ - - + + @@ -3475,8 +3455,8 @@ - - + + @@ -3511,8 +3491,8 @@ - - + + @@ -4882,7 +4862,7 @@ - + @@ -5149,7 +5129,7 @@ - + diff --git a/code/cat_visual/README.md b/code/cat_visual/README.md new file mode 100644 index 000000000..a2cb49d34 --- /dev/null +++ b/code/cat_visual/README.md @@ -0,0 +1,14 @@ +# compose_template + +Виды примитивных блоков для поддержки +* checkbox +* radio +* text choice +* different toggles + +Interfaces: +* Zoomable +* Positioned +* ZIndexed +* Grouped +* \ No newline at end of file diff --git a/code/cat_visual/build.gradle.kts b/code/cat_visual/build.gradle.kts new file mode 100644 index 000000000..7586c8f9e --- /dev/null +++ b/code/cat_visual/build.gradle.kts @@ -0,0 +1,49 @@ +import org.jetbrains.compose.compose +import org.jetbrains.compose.desktop.application.dsl.TargetFormat +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + kotlin + id("org.jetbrains.compose") version "1.3.1" + kotlin("plugin.serialization") version "1.8.0" +} + +group = "fbme" +version = "1.0" + +repositories { + google() + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") +} + +dependencies { + implementation(compose.desktop.currentOs) + implementation("com.hierynomus:asn-one:0.5.0") + implementation(kotlin("stdlib-jdk8")) + implementation("io.github.pdvrieze.xmlutil:serialization-jvm:0.84.3") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") +} + +tasks.withType { + kotlinOptions.jvmTarget = "11" +} + +compose.desktop { + application { + mainClass = "MainKt" + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) + packageName = "compose" + packageVersion = "1.0.0" + } + } +} +val compileKotlin: KotlinCompile by tasks +compileKotlin.kotlinOptions { + jvmTarget = "1.8" +} +val compileTestKotlin: KotlinCompile by tasks +compileTestKotlin.kotlinOptions { + jvmTarget = "1.8" +} \ No newline at end of file diff --git a/code/cat_visual/gradle.properties b/code/cat_visual/gradle.properties new file mode 100644 index 000000000..7fc6f1ff2 --- /dev/null +++ b/code/cat_visual/gradle.properties @@ -0,0 +1 @@ +kotlin.code.style=official diff --git a/code/cat_visual/gradlew b/code/cat_visual/gradlew new file mode 100755 index 000000000..744e882ed --- /dev/null +++ b/code/cat_visual/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MSYS* | MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/code/cat_visual/gradlew.bat b/code/cat_visual/gradlew.bat new file mode 100644 index 000000000..ac1b06f93 --- /dev/null +++ b/code/cat_visual/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/code/cat_visual/settings.gradle.kts b/code/cat_visual/settings.gradle.kts new file mode 100644 index 000000000..bbba57708 --- /dev/null +++ b/code/cat_visual/settings.gradle.kts @@ -0,0 +1,10 @@ +pluginManagement { + repositories { + google() + gradlePluginPortal() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + } + +} +rootProject.name = "compose" + diff --git a/code/cat_visual/src/main/kotlin/Main.kt b/code/cat_visual/src/main/kotlin/Main.kt new file mode 100644 index 000000000..f5710a74d --- /dev/null +++ b/code/cat_visual/src/main/kotlin/Main.kt @@ -0,0 +1,79 @@ +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application +import androidx.compose.ui.window.rememberWindowState +import canvas.items.CustomItem +import connection.* +import connection.field.BoolField +import connection.field.TYPE_ID +import example.COUNTER.CounterLampHMI +import example.WATER_TANK.System +import example.WATER_TANK.WaterTank +import serializer.PlainMapping +import serializer.getConf +import serializer.getMapping +import serializer.getPlainMapping +import java.io.BufferedReader +import java.io.File +import java.io.InputStreamReader +import java.net.DatagramPacket +import java.net.InetAddress +import java.net.ServerSocket + +@Composable +fun CanvasContext(listFigures: SnapshotStateList) { + Scaffold { innerPadding -> + Box(Modifier.fillMaxHeight().fillMaxWidth().background(Color.Gray)) { + listFigures.forEach { f -> f.create() } + } + } +} + +fun buildMappingClient(modelFile: String, configFile: String = "", mode: String = ""): AbstractClient { + if (mode.equals("plain")) { + val modelText = File(modelFile).readText() + val mapping = getPlainMapping(modelText) + + return PlainClient(mapping) + } + + val modelText = File(modelFile).readText() + val mapping = getMapping(modelText) + val configText = File(configFile).readText() + val conf = getConf(configText) + + if (mode.equals("json")) { + return JSONClient(mapping, conf) + } + return NamedClient(mapping, conf) +} + +//val client = buildMappingClient("src/main/kotlin/example/COUNTER/COUNTER.xml", "src/main/kotlin/example/COUNTER/COUNTER_CONF.xml", "") +val client = buildMappingClient("src/main/kotlin/example/COUNTER/COUNTER.xml", "src/main/kotlin/example/COUNTER/COMMON_CONF.xml", "json") +//val client = buildMappingClient("src/main/kotlin/example/WATER_TANK/WATER_TANK.xml", "src/main/kotlin/example/WATER_TANK/WATER_TANK_CONF.xml", "") +//val client = buildMappingClient("src/main/kotlin/example/WATER_TANK/WATER_TANK.xml", "src/main/kotlin/example/WATER_TANK/COMMON_CONF.xml", "json") + + +fun main() = application { + Window( + onCloseRequest = ::exitApplication, + title = "Canvas for CAT", + state = rememberWindowState(width = 600.dp, height = 600.dp) + ) { + MaterialTheme { + CounterLampHMI(client, "1") + client.retrieveValues() + } + } +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/canvas/Canvas.kt b/code/cat_visual/src/main/kotlin/canvas/Canvas.kt new file mode 100644 index 000000000..1688219d8 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/canvas/Canvas.kt @@ -0,0 +1,16 @@ +import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.Button +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier + +@Composable +fun CustomCanvas() { + Canvas(modifier = Modifier.fillMaxSize()) { + } +} + diff --git a/code/cat_visual/src/main/kotlin/canvas/UpperPlate.kt b/code/cat_visual/src/main/kotlin/canvas/UpperPlate.kt new file mode 100644 index 000000000..12a011b5c --- /dev/null +++ b/code/cat_visual/src/main/kotlin/canvas/UpperPlate.kt @@ -0,0 +1,91 @@ +import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.Button +import androidx.compose.material.Checkbox +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.* +import androidx.compose.ui.unit.dp + +@Composable +fun UpperPlate() { + Row(horizontalArrangement = Arrangement.spacedBy(5.dp)) { + PointerButton() + } +} +@Composable +@Preview +fun PointerButton() { + var text by remember { mutableStateOf("P") } + + MaterialTheme { + Button(onClick = { + text = "O" + }) { + Text(text) + } + } +} + +@Composable +@Preview +fun RectangleButton(onClickFun: () -> Unit) { + var text by remember { mutableStateOf("R") } + + MaterialTheme { + Button(onClick = onClickFun) { + Text(text) + } + } +} + + +@Composable +@Preview +fun CheckboxButton(onClickFun: () -> Unit) { + var text by remember { mutableStateOf("CB") } + + MaterialTheme { + Button(onClick = onClickFun) { + Text(text) + } + } +} + +@Composable +@Preview +fun RadioButton(onClickFun: () -> Unit) { + var text by remember { mutableStateOf("RB") } + + MaterialTheme { + Button(onClick = onClickFun) { + Text(text) + } + } +} + +@Composable +@Preview +fun ToggleButton(onClickFun: () -> Unit) { + var text by remember { mutableStateOf("TB") } + + MaterialTheme { + Button(onClick = onClickFun) { + Text(text) + } + } +} + +@Composable +@Preview +fun DropdownButton(onClickFun: () -> Unit) { + var text by remember { mutableStateOf("DB") } + + MaterialTheme { + Button(onClick = onClickFun) { + Text(text) + } + } +} diff --git a/code/cat_visual/src/main/kotlin/canvas/items/CustomItem.kt b/code/cat_visual/src/main/kotlin/canvas/items/CustomItem.kt new file mode 100644 index 000000000..86447e4cf --- /dev/null +++ b/code/cat_visual/src/main/kotlin/canvas/items/CustomItem.kt @@ -0,0 +1,182 @@ +package canvas.items + +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.selection.selectable +import androidx.compose.material.RadioButton +import androidx.compose.material.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.input.pointer.PointerEventPass +import androidx.compose.ui.input.pointer.PointerEventType +import androidx.compose.ui.input.pointer.onPointerEvent +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.dp + +enum class ItemType { + RECTANGLE, RADIO, CHECKBOX +} + +@Composable +fun ZoomableBox( + modifier: Modifier = Modifier, + minScale: Float = 0.1f, + maxScale: Float = 5f, + content: @Composable ZoomableBoxScope.() -> Unit +) { + var scale by remember { mutableStateOf(1f) } + var offsetX by remember { mutableStateOf(0f) } + var offsetY by remember { mutableStateOf(0f) } + var size by remember { mutableStateOf(IntSize(50, 50)) } + Box( + modifier = modifier +// .clip(RectangleShape) + .onSizeChanged { size = it } + .pointerInput(Unit) { + detectTransformGestures { _, pan, zoom, _ -> + scale = maxOf(minScale, minOf(scale * zoom, maxScale)) + val maxX = (size.width * (scale - 1)) / 2 + val minX = -maxX + offsetX = maxOf(minX, minOf(maxX, offsetX + pan.x)) + val maxY = (size.height * (scale - 1)) / 2 + val minY = -maxY + offsetY = maxOf(minY, minOf(maxY, offsetY + pan.y)) + } + } + ) { + val scope = ZoomableBoxScopeImpl(scale, offsetX, offsetY) + scope.content() + println(scale) + println(offsetX) + println(offsetY) + println(size) + } +} + +interface ZoomableBoxScope { + val scale: Float + val offsetX: Float + val offsetY: Float +} + +private data class ZoomableBoxScopeImpl( + override val scale: Float, + override val offsetX: Float, + override val offsetY: Float +) : ZoomableBoxScope + + +abstract interface CustomItem { + @Composable + fun create(); +} + +class RectangleCustomItem : CustomItem { + var c = Color.Blue + @Composable + fun RectangleItem() { + var color by remember { mutableStateOf(c) } + ZoomableBox { + Box( + modifier = Modifier + .graphicsLayer( + scaleX = scale, + scaleY = scale, + translationX = offsetX, + translationY = offsetY + ) + .background(color = color) + .width(600.dp) + .height(600.dp) + ) { + } + } + + } + + @Composable + override fun create() { + return RectangleItem(); + } +} + +class CheckboxCustomItem : CustomItem { + var c = Color.Blue + @Composable + fun RectangleItem() { + var color by remember { mutableStateOf(c) } + ZoomableBox { + Box( + modifier = Modifier + .graphicsLayer( + scaleX = scale, + scaleY = scale, + translationX = offsetX, + translationY = offsetY + ) + .background(color = color) + .width(600.dp) + .height(600.dp) + ) { + } + } + + } + + @Composable + override fun create() { + return RectangleItem(); + } +} + +class RadioCustomItem : CustomItem { + var c = Color.Blue + @Composable + fun RadioItem(items: List) { + var color by remember { mutableStateOf(c) } + var selectedValue by remember { mutableStateOf("") } + val isSelectedItem: (String) -> Boolean = { selectedValue == it } + val onChangeState: (String) -> Unit = { selectedValue = it } + ZoomableBox { + items.forEach { item -> + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.selectable( + selected = isSelectedItem(item), + onClick = { onChangeState(item) }, + role = Role.RadioButton + ).padding(8.dp) + ) { + RadioButton( + selected = isSelectedItem(item), + onClick = null + ) + Text( + text = item, + modifier = Modifier.fillMaxWidth() + ) + } + } + } + + } + + @Composable + override fun create() { + val items = listOf("Item1", "Item2") + return RadioItem(items); + } +} diff --git a/code/cat_visual/src/main/kotlin/connection/ConnectionFieldRegistry.kt b/code/cat_visual/src/main/kotlin/connection/ConnectionFieldRegistry.kt new file mode 100644 index 000000000..96b524370 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/connection/ConnectionFieldRegistry.kt @@ -0,0 +1,45 @@ +package connection + +import connection.field.ConnectionField +import connection.field.TYPE_ID +import connection.provider.ConnectionProvider +import connection.provider.UDPConnectionProvider + + +class ConnectionFieldRegistry { + private val providerRegistry: MutableMap, ConnectionProvider> = mutableMapOf() + private val fieldRegistry: MutableMap,ConnectionProvider> > = mutableMapOf() + + fun getConnection(name: String, type: TYPE_ID, host: String, port: Int): Pair,ConnectionProvider> { + if (name in fieldRegistry) { + return fieldRegistry[name]!! + } + val providerAddress = Pair(host, port) + val provider = providerRegistry.computeIfAbsent(providerAddress, { UDPConnectionProvider(port, host) }) + val field = ConnectionField.create(type) + val connection = Pair(field, provider) + fieldRegistry[name] = connection + return connection + } + + fun getConnection(name: String): Pair,ConnectionProvider>? { + if (name in fieldRegistry) { + return fieldRegistry[name]!! + } + return null + } + + fun getField(name: String): ConnectionField? { + if (name in fieldRegistry) { + return fieldRegistry[name]!!.first + } + return null + } + + fun getConnector(hostPort: Pair): ConnectionProvider? { + if (hostPort in providerRegistry) { + return providerRegistry[hostPort]!! + } + return null + } +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/connection/clients.kt b/code/cat_visual/src/main/kotlin/connection/clients.kt new file mode 100644 index 000000000..99a76bee1 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/connection/clients.kt @@ -0,0 +1,174 @@ +package connection + +import connection.field.ConnectionField +import connection.field.StringField +import connection.field.TYPE_ID +import connection.provider.ConnectionProvider +import kotlinx.serialization.Serializable +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import serializer.Conf +import serializer.Mapping +import serializer.PlainMapping +import java.nio.ByteBuffer + +abstract class AbstractClient() { + abstract fun getField(name: String): ConnectionField?; + abstract fun sendValue(name: String) + abstract fun retrieveValues(callbacks: Map Unit> = mapOf(), isInput: Boolean = true) +} + +abstract class UDPClient(): AbstractClient() { + protected val registry: ConnectionFieldRegistry = ConnectionFieldRegistry() + val inputs: MutableSet = mutableSetOf() + val outputs: MutableSet = mutableSetOf() + val inputConnections: MutableList = mutableListOf() + val outputConnections: MutableList = mutableListOf() + + override fun getField(name: String): ConnectionField? { + return registry.getConnection(name)?.first + } +} + +class PlainClient(mapping: PlainMapping): UDPClient() { + init { + mapping.inputs.inputs.forEach({ + registry.getConnection(it.name, TYPE_ID.valueOf(it.type), it.host, it.port) + inputs.add(it.name) + inputConnections.add(registry.getConnection(it.name)!!.second) + }) + mapping.outputs.outputs.forEach({ + registry.getConnection(it.name, TYPE_ID.valueOf(it.type), it.host, it.port) + outputs.add(it.name) + outputConnections.add(registry.getConnection(it.name)!!.second) + }) + } + + override fun sendValue(name: String) { + val fieldConnector = registry.getConnection(name) + val field = fieldConnector!!.first + val connector = fieldConnector.second + connector.request(field, ByteArray(0)) + } + + override fun retrieveValues(callbacks: Map Unit>, isInput: Boolean) { + val units = if (isInput) inputs else outputs + units.forEach{ + val fieldConnector = registry.getConnection(it) + val field = fieldConnector!!.first + val connector = fieldConnector.second + connector.response({ba -> Pair(Pair(field, ba), it)}, callbacks=callbacks) + } + } +} + +class NamedClient(mapping: Mapping, conf: Conf): UDPClient() { + val nameField: ConnectionField = StringField() + val inputsTypes: MutableMap> = mutableMapOf() + val outputsTypes: MutableMap> = mutableMapOf() + + init { + conf.inputs.inputs.forEach({ + inputsTypes.put(TYPE_ID.valueOf(it.type), Pair(it.host, it.port)) + }) + conf.outputs.outputs.forEach({ + outputsTypes.put(TYPE_ID.valueOf(it.type), Pair(it.host, it.port)) + }) + mapping.inputs.inputs.forEach { + val hostPort = inputsTypes.get(TYPE_ID.valueOf(it.type)) + registry.getConnection(it.name, TYPE_ID.valueOf(it.type), hostPort!!.first, hostPort.second) + inputs.add(it.name) + } + mapping.outputs.outputs.forEach({ + val hostPort = outputsTypes.get(TYPE_ID.valueOf(it.type)) + registry.getConnection(it.name, TYPE_ID.valueOf(it.type), hostPort!!.first, hostPort.second) + outputs.add(it.name) + }) + inputsTypes.values.forEach({ + inputConnections.add(registry.getConnector(it)!!) + }) + outputsTypes.values.forEach({ + outputConnections.add(registry.getConnector(it)!!) + }) + } + + override fun sendValue(name: String) { + val fieldConnector = registry.getConnection(name) + val field = fieldConnector!!.first + val connector = fieldConnector.second + nameField.setValue(name) + connector.request(field, nameField.getFromFBValue()) + } + + override fun retrieveValues(callbacks: Map Unit>, isInput: Boolean) { + val units = if (isInput) inputConnections else outputConnections + units.forEach { + it.response({ ba -> + var size = ByteBuffer.wrap(ba).getShort(1) + val name = String(ba.copyOfRange(3, 3 + size.toInt())) + val offset = 3 + size.toInt() + registry.getConnection(name) + val field = registry.getConnection(name)!!.first + Pair(Pair(field, ba.copyOfRange(offset, ba.size)), name) + }, callbacks= callbacks) + } + } +} + +class JSONClient(mapping: Mapping, conf: Conf): UDPClient() { + val MSG_TYPE: TYPE_ID = TYPE_ID.STRING + val nameField: ConnectionField = StringField() + val inputsTypes: MutableMap> = mutableMapOf() + val outputsTypes: MutableMap> = mutableMapOf() + + @Serializable + data class Msg(val NAME: String, val TYPE: String, val VALUE: String) + init { + conf.inputs.inputs.forEach({ + inputsTypes.put(TYPE_ID.valueOf(it.type), Pair(it.host, it.port)) + }) + conf.outputs.outputs.forEach({ + outputsTypes.put(TYPE_ID.valueOf(it.type), Pair(it.host, it.port)) + }) + mapping.inputs.inputs.forEach { + val hostPort = inputsTypes.get(MSG_TYPE) + registry.getConnection("${mapping.id}#${it.name}", TYPE_ID.valueOf(it.type), hostPort!!.first, hostPort.second) + inputs.add(it.name) + } + mapping.outputs.outputs.forEach({ + val hostPort = outputsTypes.get(MSG_TYPE) + registry.getConnection("${mapping.id}#${it.name}", TYPE_ID.valueOf(it.type), hostPort!!.first, hostPort.second) + outputs.add(it.name) + }) + conf.inputs.inputs.forEach({ + inputConnections.add(registry.getConnector(Pair(it.host, it.port))!!) + }) + conf.outputs.outputs.forEach({ + outputConnections.add(registry.getConnector(Pair(it.host, it.port))!!) + }) + } + + override fun sendValue(name: String) { + val fieldConnector = registry.getConnection(name) + val field = fieldConnector!!.first + val connector = fieldConnector.second + val data = Msg(name, field.getTypeID().name, field.getMsgValue()) + val msg = Json.encodeToString(data) + connector.request(msg) + } + + override fun retrieveValues(callbacks: Map Unit>, isInput: Boolean) { + val units = if (isInput) inputConnections else outputConnections + units.forEach { + it.response({ + val msg = Json.decodeFromString(it) + val field = registry.getField(msg.NAME)!! + println("GETTING ${field.getTypeID()}") + field.getFBValue(msg.VALUE) + println("GOT ${field.getValue()}") + msg.NAME + }, true, callbacks= callbacks) + } + } +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/connection/field/BoolField.kt b/code/cat_visual/src/main/kotlin/connection/field/BoolField.kt new file mode 100644 index 000000000..a2384a409 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/connection/field/BoolField.kt @@ -0,0 +1,37 @@ +package connection.field + +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf + +data class BoolField(override var content:Boolean = false, override val contentState: MutableState = mutableStateOf(content)): ConnectionField(content, contentState) { + + val TRUE_VALUE:Byte = 65 + val FALSE_VALUE:Byte = 64 + + + override fun getFromFBValue():ByteArray { + val res = ByteArray(1) + res[0] = if (content) TRUE_VALUE else FALSE_VALUE + return res + } + + override fun getFBValue(d: ByteArray) { + setValue(d[0] == TRUE_VALUE) + } + + override fun getFBValue(d: String) { + setValue(d.equals("TRUE")); + } + + override fun getMsgValue(): String { + if (content) { + return "TRUE"; + } + return "FALSE"; + } + + override fun getTypeID(): TYPE_ID { + return TYPE_ID.BOOL + } + +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/connection/field/ConnectionField.kt b/code/cat_visual/src/main/kotlin/connection/field/ConnectionField.kt new file mode 100644 index 000000000..4d539ee6e --- /dev/null +++ b/code/cat_visual/src/main/kotlin/connection/field/ConnectionField.kt @@ -0,0 +1,61 @@ +package connection.field + +import androidx.compose.runtime.MutableState +import kotlinx.coroutines.Dispatchers +import java.lang.UnsupportedOperationException + + +abstract class ConnectionField(open var content: V, open val contentState: MutableState) { + companion object { + fun create(type: TYPE_ID):ConnectionField { +// println(type) + return when (type) { + TYPE_ID.BOOL -> BoolField() + TYPE_ID.REAL -> FloatField(0f) + TYPE_ID.LREAL -> DoubleField(0.0) + TYPE_ID.STRING -> StringField() + TYPE_ID.SINT -> SIntField(0) + TYPE_ID.USINT -> USIntField(0u) + TYPE_ID.INT -> IntField(0) + TYPE_ID.UINT -> UIntField(0u) + TYPE_ID.DINT -> DIntField(0) + TYPE_ID.UDINT -> UDIntField(0u) + TYPE_ID.LINT -> LIntField(0) + TYPE_ID.ULINT -> ULIntField(0u) + else -> {throw UnsupportedOperationException("Unsupported type literal")} + } + } + } + abstract fun getFromFBValue():ByteArray + abstract fun getMsgValue():String + abstract fun getFBValue(d: ByteArray) + abstract fun getFBValue(d: String) + abstract fun getTypeID():TYPE_ID + fun getValue():V { + return content + } + fun setValue(v:V) { + println("!!! BEFORE SET ${content}") + content = v + contentState.value = content + println("!!! SET ${content}") + } +} + +enum class TYPE_ID(val code: Int) { + BOOL(65), + SINT(66), + INT(67), + DINT(68), + LINT(69), + USINT(70), + UINT(71), + UDINT(72), + ULINT(73), + REAL(74), + LREAL(75), + STRING(80), + WSTRING(85), + DATE_AND_TIME(79), + ARRAY(118) +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/connection/field/DoubleField.kt b/code/cat_visual/src/main/kotlin/connection/field/DoubleField.kt new file mode 100644 index 000000000..4c456030b --- /dev/null +++ b/code/cat_visual/src/main/kotlin/connection/field/DoubleField.kt @@ -0,0 +1,36 @@ +package connection.field; + +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.sourceInformation +import java.nio.ByteBuffer +import java.util.* + +data class DoubleField(var defaultValue:Double, override var content:Double = 0.0, override val contentState: MutableState = mutableStateOf(defaultValue)): ConnectionField(content, contentState) { + + override fun getFromFBValue(): ByteArray { + val bytes: ByteArray = ByteBuffer.allocate(5).put(getTypeID().code.toByte()).putDouble(content).array() + return bytes + } + + override fun getMsgValue(): String { + return getValue().toString() + } + + override fun getFBValue(d: ByteArray) { + var buf = ByteBuffer.wrap(d) + + if (buf[0] == TYPE_ID.LREAL.code.toByte()) { + setValue(ByteBuffer.wrap(d).getDouble(1)) + } + } + + override fun getFBValue(d: String) { + setValue(d.toDouble()) + } + + override fun getTypeID(): TYPE_ID { +// TODO("add support of just SINT") + return TYPE_ID.LREAL + } +} diff --git a/code/cat_visual/src/main/kotlin/connection/field/FloatField.kt b/code/cat_visual/src/main/kotlin/connection/field/FloatField.kt new file mode 100644 index 000000000..e44ab1d30 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/connection/field/FloatField.kt @@ -0,0 +1,33 @@ +package connection.field; + +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import java.nio.ByteBuffer + +data class FloatField(var defaultValue:Float, override var content:Float = 0f, override val contentState: MutableState = mutableStateOf(defaultValue)): ConnectionField(content, contentState) { + + override fun getFromFBValue(): ByteArray { + val bytes: ByteArray = ByteBuffer.allocate(5).put(getTypeID().code.toByte()).putFloat(content).array() + return bytes + } + + override fun getMsgValue(): String { + return getValue().toString() + } + + override fun getFBValue(d: ByteArray) { + var buf = ByteBuffer.wrap(d) + + if (buf[0] == TYPE_ID.REAL.code.toByte()) { + setValue(ByteBuffer.wrap(d).getFloat(1)) + } + } + + override fun getFBValue(d: String) { + setValue(d.toFloat()) + } + + override fun getTypeID(): TYPE_ID { + return TYPE_ID.REAL + } +} diff --git a/code/cat_visual/src/main/kotlin/connection/field/IntField.kt b/code/cat_visual/src/main/kotlin/connection/field/IntField.kt new file mode 100644 index 000000000..4ba3719ca --- /dev/null +++ b/code/cat_visual/src/main/kotlin/connection/field/IntField.kt @@ -0,0 +1,238 @@ +package connection.field; + +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import java.nio.ByteBuffer + +data class SIntField(var defaultValue:Byte, override var content:Byte = 0, override val contentState: MutableState = mutableStateOf(defaultValue)): ConnectionField(content, contentState) { + + override fun getFromFBValue(): ByteArray { + val bytes: ByteArray = ByteBuffer.allocate(5).put(getTypeID().code.toByte()).put(content).array() + return bytes + } + + override fun getMsgValue(): String { + return getValue().toString() + } + + override fun getFBValue(d: ByteArray) { + var buf = ByteBuffer.wrap(d) + + if (buf[0] == TYPE_ID.SINT.code.toByte()) { + setValue(ByteBuffer.wrap(d).get(1)) + } + println(d.contentToString()) + } + + override fun getFBValue(d: String) { + setValue(d.toByte()) + } + + override fun getTypeID(): TYPE_ID { + return TYPE_ID.SINT + } +} + +data class USIntField(var defaultValue:UByte, override var content:UByte = 0u, override val contentState: MutableState = mutableStateOf(defaultValue)): ConnectionField(content, contentState) { + + override fun getFromFBValue(): ByteArray { + val bytes: ByteArray = ByteBuffer.allocate(5).put(getTypeID().code.toByte()).put(content.toByte()).array() + return bytes + } + + override fun getMsgValue(): String { + return getValue().toString() + } + + override fun getFBValue(d: ByteArray) { + var buf = ByteBuffer.wrap(d) + + if (buf[0] == TYPE_ID.UINT.code.toByte()) { + setValue(ByteBuffer.wrap(d).get(1).toUByte()) + } + println(d.contentToString()) + } + + override fun getFBValue(d: String) { + setValue(d.toUByte()) + } + + override fun getTypeID(): TYPE_ID { + return TYPE_ID.USINT + } +} + +data class IntField(var defaultValue:Short, override var content:Short = 0, override val contentState: MutableState = mutableStateOf(defaultValue)): ConnectionField(content, contentState) { + + override fun getFromFBValue(): ByteArray { + val bytes: ByteArray = ByteBuffer.allocate(5).put(getTypeID().code.toByte()).putShort(content).array() + return bytes + } + + override fun getMsgValue(): String { + return getValue().toString() + } + + override fun getFBValue(d: ByteArray) { + var buf = ByteBuffer.wrap(d) + + if (buf[0] == TYPE_ID.INT.code.toByte()) { + setValue(ByteBuffer.wrap(d).getShort(1)) + } + println(d.contentToString()) + } + + override fun getFBValue(d: String) { + setValue(d.toShort()) + } + + override fun getTypeID(): TYPE_ID { + return TYPE_ID.INT + } +} + +data class UIntField(var defaultValue:UShort, override var content:UShort = 0u, override val contentState: MutableState = mutableStateOf(defaultValue)): ConnectionField(content, contentState) { + + override fun getFromFBValue(): ByteArray { + val bytes: ByteArray = ByteBuffer.allocate(5).put(getTypeID().code.toByte()).putShort(content.toShort()).array() + return bytes + } + + override fun getMsgValue(): String { + return getValue().toString() + } + + override fun getFBValue(d: ByteArray) { + var buf = ByteBuffer.wrap(d) + + if (buf[0] == TYPE_ID.UINT.code.toByte()) { + setValue(ByteBuffer.wrap(d).getShort(1).toUShort()) + } + println(d.contentToString()) + } + + override fun getFBValue(d: String) { + setValue(d.toUShort()) + } + + override fun getTypeID(): TYPE_ID { + return TYPE_ID.UINT + } +} + +data class DIntField(var defaultValue:Int, override var content:Int = 0, override val contentState: MutableState = mutableStateOf(defaultValue)): ConnectionField(content, contentState) { + + override fun getFromFBValue(): ByteArray { + val bytes: ByteArray = ByteBuffer.allocate(5).put(getTypeID().code.toByte()).putInt(content).array() + return bytes + } + + override fun getMsgValue(): String { + return getValue().toString() + } + + override fun getFBValue(d: ByteArray) { + var buf = ByteBuffer.wrap(d) + + if (buf[0] == TYPE_ID.DINT.code.toByte()) { + setValue(ByteBuffer.wrap(d).getInt(1)) + } + println(d.contentToString()) + } + + override fun getFBValue(d: String) { + setValue(d.toInt()) + } + + override fun getTypeID(): TYPE_ID { + return TYPE_ID.DINT + } +} + +data class UDIntField(var defaultValue:UInt, override var content:UInt = 0u, override val contentState: MutableState = mutableStateOf(defaultValue)): ConnectionField(content, contentState) { + + override fun getFromFBValue(): ByteArray { + val bytes: ByteArray = ByteBuffer.allocate(9).put(getTypeID().code.toByte()).putInt(content.toInt()).array() + return bytes + } + + override fun getMsgValue(): String { + return getValue().toString() + } + + override fun getFBValue(d: ByteArray) { + var buf = ByteBuffer.wrap(d) + + if (buf[0] == TYPE_ID.UDINT.code.toByte()) { + setValue(ByteBuffer.wrap(d).getInt(1).toUInt()) + } + println(d.contentToString()) + } + + override fun getFBValue(d: String) { + setValue(d.toUInt()) + } + + override fun getTypeID(): TYPE_ID { + return TYPE_ID.UDINT + } +} + + +data class LIntField(var defaultValue:Long, override var content:Long = 0, override val contentState: MutableState = mutableStateOf(defaultValue)): ConnectionField(content, contentState) { + + override fun getFromFBValue(): ByteArray { + val bytes: ByteArray = ByteBuffer.allocate(5).put(getTypeID().code.toByte()).putLong(content).array() + return bytes + } + + override fun getMsgValue(): String { + return getValue().toString() + } + + override fun getFBValue(d: ByteArray) { + var buf = ByteBuffer.wrap(d) + + if (buf[0] == TYPE_ID.LINT.code.toByte()) { + setValue(ByteBuffer.wrap(d).getLong(1)) + } + println(d.contentToString()) + } + + override fun getFBValue(d: String) { + setValue(d.toLong()) + } + + override fun getTypeID(): TYPE_ID { + return TYPE_ID.LINT + } +} + +data class ULIntField(var defaultValue:ULong, override var content:ULong = 0uL, override val contentState: MutableState = mutableStateOf(defaultValue)): ConnectionField(content, contentState) { + + override fun getFromFBValue(): ByteArray { + val bytes: ByteArray = ByteBuffer.allocate(9).put(getTypeID().code.toByte()).putLong(content.toLong()).array() + return bytes + } + + override fun getMsgValue(): String { + return getValue().toString() + } + + override fun getFBValue(d: ByteArray) { + var buf = ByteBuffer.wrap(d) + + if (buf[0] == TYPE_ID.UDINT.code.toByte()) { + setValue(ByteBuffer.wrap(d).getLong(1).toULong()) + } + println(d.contentToString()) + } + + override fun getFBValue(d: String) { + setValue(d.toULong()) + } + + override fun getTypeID(): TYPE_ID { + return TYPE_ID.ULINT + } +} diff --git a/code/cat_visual/src/main/kotlin/connection/field/StringField.kt b/code/cat_visual/src/main/kotlin/connection/field/StringField.kt new file mode 100644 index 000000000..d57e7afff --- /dev/null +++ b/code/cat_visual/src/main/kotlin/connection/field/StringField.kt @@ -0,0 +1,31 @@ +package connection.field + +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import java.nio.ByteBuffer + +data class StringField(override var content:String = "", override val contentState: MutableState = mutableStateOf(content)): ConnectionField(content, contentState) { + override fun getFromFBValue(): ByteArray { + val contentBytes: ByteArray = content.toByteArray() + return ByteBuffer.allocate(contentBytes.size + 3).put(TYPE_ID.STRING.code.toByte()).putShort(content.length.toShort()).put(contentBytes).array() + } + + override fun getMsgValue(): String { + return content + } + + override fun getFBValue(d: ByteArray) { + var size = ByteBuffer.wrap(d).getShort(1) + setValue(String(d.copyOfRange(3, 3 + size.toInt()))) + println(d.contentToString()) + } + + override fun getFBValue(d: String) { + setValue(d) + } + + override fun getTypeID(): TYPE_ID { + return TYPE_ID.STRING + } + +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/connection/field/WStringField.kt b/code/cat_visual/src/main/kotlin/connection/field/WStringField.kt new file mode 100644 index 000000000..41ef84f6d --- /dev/null +++ b/code/cat_visual/src/main/kotlin/connection/field/WStringField.kt @@ -0,0 +1,31 @@ +package connection.field + +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import java.nio.ByteBuffer + +data class WStringField(override var content:String = "", override val contentState: MutableState = mutableStateOf(content)): ConnectionField(content, contentState) { + override fun getFromFBValue(): ByteArray { + val contentBytes: ByteArray = content.toByteArray(Charsets.UTF_16) + return ByteBuffer.allocate(contentBytes.size * 2 + 3).put(getTypeID().code.toByte()).putShort((content.length.toShort() * 2).toShort()).put(contentBytes).array() + } + + override fun getMsgValue(): String { + TODO("Not yet implemented") + } + + override fun getFBValue(d: ByteArray) { + var size = ByteBuffer.wrap(d).getShort(1) + setValue(String(d.copyOfRange(3, 3 + size.toInt()), Charsets.UTF_16)) + println(d.contentToString()) + } + + override fun getFBValue(d: String) { + TODO("Not yet implemented") + } + + override fun getTypeID(): TYPE_ID { + return TYPE_ID.WSTRING + } + +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/connection/provider/ConnectionProvider.kt b/code/cat_visual/src/main/kotlin/connection/provider/ConnectionProvider.kt new file mode 100644 index 000000000..a81d45400 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/connection/provider/ConnectionProvider.kt @@ -0,0 +1,89 @@ +package connection.provider + +import connection.ConnectionFieldRegistry +import connection.field.ConnectionField +import connection.field.TYPE_ID +import java.io.BufferedReader +import java.io.InputStreamReader +import java.io.PrintWriter +import java.net.DatagramPacket +import java.net.InetAddress +import java.net.MulticastSocket +import java.net.ServerSocket +import java.net.Socket +import java.nio.ByteBuffer +import kotlin.concurrent.thread + +abstract class ConnectionProvider(private val port: Int, private val host: String = "225.0.0.1") { + abstract fun response(fieldGetter: (ByteArray) -> Pair, ByteArray>, String>, ping:Long = 500, callbacks: Map Unit> = mapOf()) + abstract fun response(fieldSetter: (String) -> String, log: Boolean, ping:Long = 500, callbacks: Map Unit> = mapOf()) + abstract fun request(field: ConnectionField, prefix: ByteArray) + abstract fun request(msg: String) +} + +class UDPConnectionProvider(private val port: Int, private val host: String = "225.0.0.1"): ConnectionProvider(port, host) { + val group = InetAddress.getByName(host); + val socket = MulticastSocket(port); + + + init { + socket.joinGroup(group) + } + + var ind = 0 + + override fun response(fieldGetter: (ByteArray) -> Pair, ByteArray>, String>, ping:Long, callbacks: Map Unit>) { + thread { + while (true) { + val buf = ByteArray(1024); + val recv = DatagramPacket(buf, buf.size); + socket.receive(recv) + val fieldData = fieldGetter(recv.data) + val field = fieldData.first.first + val data = fieldData.first.second + val name = fieldData.second + println("GETTING ${field.getTypeID()}") + field.getFBValue(data) + callbacks.getOrDefault(name, {})() + println("GOT ${field.getValue()}") + Thread.sleep(ping) + } + } + } + + override fun request(field: ConnectionField, prefix: ByteArray) { + println("SENDING ${field.getValue()}") + var msg = field.getFromFBValue() + msg = prefix.plus(msg) + val hi = DatagramPacket(msg, msg.size, + group, port); + socket.send(hi) + } + + override fun request(msgJ: String) { + println("SENDING ${msgJ}") + val contentBytes = msgJ.toByteArray() + val msg = ByteBuffer.allocate(contentBytes.size + 3).put(TYPE_ID.STRING.code.toByte()).putShort(msgJ.length.toShort()).put(contentBytes).array() + val hi = DatagramPacket(msg, msg.size, + group, port); + socket.send(hi) + } + + override fun response(fieldSetter: (String) -> String, log: Boolean, ping:Long, callbacks: Map Unit>) { + thread { + while (true) { + + val buf = ByteArray(1024); + val recv = DatagramPacket(buf, buf.size); + socket.receive(recv) + var size = ByteBuffer.wrap(recv.data).getShort(1) + var msg = String(recv.data.copyOfRange(3, 3 + size.toInt())) + if (log) println("MESSAGE $msg") + val name = fieldSetter(msg) + callbacks.getOrDefault(name, {})() + Thread.sleep(ping) + } + } + } + +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/example/COUNTER/COMMON_CONF.xml b/code/cat_visual/src/main/kotlin/example/COUNTER/COMMON_CONF.xml new file mode 100644 index 000000000..b887ebd85 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/example/COUNTER/COMMON_CONF.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/example/COUNTER/COUNT.xml b/code/cat_visual/src/main/kotlin/example/COUNTER/COUNT.xml new file mode 100644 index 000000000..e69de29bb diff --git a/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER.kt b/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER.kt new file mode 100644 index 000000000..309fe7173 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER.kt @@ -0,0 +1,44 @@ +package example.COUNTER + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import connection.AbstractClient +import connection.ConnectionFieldRegistry +import lib.elements.getters.Bulb +import lib.elements.getters.TextBox +import lib.elements.setters.Checkbox +import lib.elements.setters.Toggle +import lib.visual.PositionedBox + + +@Composable +fun LampHMI(client: AbstractClient, id: String) { + Bulb("$id#LAMP", client) +} +@Composable +fun ToggleHMI(client: AbstractClient, id: String) { + Toggle("$id#TOGGLE", client) +} + +@Composable +fun CounterHMI(client: AbstractClient, id: String) { + Row { + Text("Count of changes: ") + TextBox("$id#COUNT", client, Modifier.width(40.dp).height(30.dp).background(Color.LightGray)) +// PositionedBox( children = {Indicator("count", "225.0.0.2", 65003, registry, 0, 100, 1000.dp)}, x = 100, y = 100) + } +} + +@Composable +fun CounterLampHMI(client: AbstractClient, id: String) { + PositionedBox(children = {CounterHMI(client, "1")}, x = 200) + LampHMI(client, "1") + PositionedBox(children = {example.COUNTER.ToggleHMI(client, "1")}, x = 300, y = 100) +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER.xml b/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER.xml new file mode 100644 index 000000000..869c96923 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER_CONF.xml b/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER_CONF.xml new file mode 100644 index 000000000..bb9432301 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER_CONF.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER_PLAIN.xml b/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER_PLAIN.xml new file mode 100644 index 000000000..8998ad64d --- /dev/null +++ b/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER_PLAIN.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/example/COUNTER/LAMP.xml b/code/cat_visual/src/main/kotlin/example/COUNTER/LAMP.xml new file mode 100644 index 000000000..869c96923 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/example/COUNTER/LAMP.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/example/COUNTER/TOGGLE.xml b/code/cat_visual/src/main/kotlin/example/COUNTER/TOGGLE.xml new file mode 100644 index 000000000..e69de29bb diff --git a/code/cat_visual/src/main/kotlin/example/INCHOICE/INCHOICE.kt b/code/cat_visual/src/main/kotlin/example/INCHOICE/INCHOICE.kt new file mode 100644 index 000000000..b835fd35d --- /dev/null +++ b/code/cat_visual/src/main/kotlin/example/INCHOICE/INCHOICE.kt @@ -0,0 +1,75 @@ +//package example.INCHOICE +// +//import androidx.compose.foundation.background +//import androidx.compose.foundation.layout.Box +//import androidx.compose.foundation.layout.Column +//import androidx.compose.foundation.layout.height +//import androidx.compose.foundation.layout.width +//import androidx.compose.material.Switch +//import androidx.compose.material.Text +//import androidx.compose.runtime.* +//import androidx.compose.ui.Modifier +//import androidx.compose.ui.graphics.Color +//import androidx.compose.ui.unit.dp +//import connection.field.BoolField +//import connection.field.ConnectionField +//import connection.field.StringField +//import connection.provider.ConnectionProvider +// +//val field: ConnectionField = BoolField() +//val toggleField: ConnectionField = BoolField() +//val ipField: ConnectionField = StringField() +// +//var connectionProvider: ConnectionProvider = ConnectionProvider(field, 65001) +//var connectionProviderToggle: ConnectionProvider = ConnectionProvider(toggleField, 65000) +//var connectionProviderIpText: ConnectionProvider = ConnectionProvider(ipField, 65002) +// +// +//val ping = 1 +// +//@Composable +//fun LampToggle() { +// val checkedState = remember { toggleField } +// Switch( +// checked = checkedState.contentState.value, +// onCheckedChange = { +// checkedState.setValue(it) +// connectionProviderToggle.request()} +// ) +//} +// +// +//val trueColor = Color.Yellow +//val falseColor = Color.Black +// +// +//@Composable +//fun Lamp_HMI() { +// fun getColor(s: Boolean): Color { +// if (s) { +// return trueColor +// } else { +// return falseColor +// } +// } +// +// +// var checkedState = remember{ field } +// Column { +// Box( +// modifier = Modifier.background(color = if (checkedState.contentState.value) trueColor else falseColor) +// .width(600.dp) +// .height(600.dp) +// ) { +// connectionProvider.response() +// } +// Box( +// modifier = Modifier.background(color = Color.Gray) +// .width(200.dp) +// .height(200.dp) +// ) { +// Text(ipField.contentState.value) +// connectionProviderIpText.response() +// } +// } +//} diff --git a/code/cat_visual/src/main/kotlin/example/INCHOICE/INCHOICE_HMI.cnv.xml b/code/cat_visual/src/main/kotlin/example/INCHOICE/INCHOICE_HMI.cnv.xml new file mode 100644 index 000000000..5297eeccc --- /dev/null +++ b/code/cat_visual/src/main/kotlin/example/INCHOICE/INCHOICE_HMI.cnv.xml @@ -0,0 +1,17 @@ + + + + CHOICES + + + I + + + + + + + + + + \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/example/WATER_TANK/WATER_TANK.kt b/code/cat_visual/src/main/kotlin/example/WATER_TANK/WATER_TANK.kt new file mode 100644 index 000000000..3d66990ed --- /dev/null +++ b/code/cat_visual/src/main/kotlin/example/WATER_TANK/WATER_TANK.kt @@ -0,0 +1,121 @@ +package example.WATER_TANK + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex +import connection.AbstractClient +import connection.ConnectionFieldRegistry +import lib.elements.* +import lib.elements.getters.Bulb +import lib.elements.getters.LightingText +import lib.elements.getters.VerticalIndicator +import lib.elements.setters.RoundKnob +import lib.visual.PositionedBox + +@Composable +fun WaterTank(client: AbstractClient) { + + Box { + PositionedBox(@Composable { LampIndicator("lampIndicator1", client) }, 120, 190) + PositionedBox(@Composable { LampIndicator("lampIndicator2", client) }, 120, 380) + PositionedBox(@Composable { PipeConnector(Color.Gray, 100.dp, 30.dp) }, 150, 100) + PositionedBox(@Composable { Figure(RoundedCornerShape(100.dp), + modifier = Modifier.size(200.dp, 300.dp) + .background(brush = METALLIC_BRUSH, shape = RoundedCornerShape(20.dp)) + ) + }, 200, 150, zIndex = 2f) + PositionedBox(@Composable { Pipe(Color.Gray, 100.dp, 30.dp) }, 400, 400) + PositionedBox(@Composable { + VerticalIndicator( + "tankIndicator", client, + color = Color.Blue, step = 20f, minTemperature = 0f, maxTemperature = 100f, + trackHeight = 200.dp, indicatorWidth = 20.dp + ) + }, 300, 200, zIndex = 3f) + } +} + +@Composable +fun LampIndicator(name: String, client: AbstractClient, pipeSize: Dp = 30.dp) { + Row { + PositionedBox(@Composable { + Bulb( + name, + client, + size = pipeSize, + borderWidth = 4.dp, + modifier = Modifier.zIndex(2f) + ) + }, pipeSize.value / 2) + Pipe(Color.Gray, 100.dp, pipeSize) + } +} + + +@Composable +fun System(client: AbstractClient) { + PositionedBox(@Composable { + RoundKnob( + "knob1", + client, + 0..100, + 0f, + knobSize = 60.dp, + knobColor = Color.Red + ) + }, 10, 50) + PositionedBox(@Composable { + RoundKnob( + "knob2", + client, + 0..100, + 0f, + knobSize = 60.dp, + knobColor = Color.Blue + ) + }, 10, 120) + WaterTank(client) + PositionedBox(@Composable { + VerticalIndicator( + "outputIndicator", client, + color = Color.Red, step = 20f, minTemperature = 0f, maxTemperature = 100f, + trackHeight = 200.dp, indicatorWidth = 20.dp + ) + }, 440, 150, zIndex = 3f) + PositionedBox(@Composable { + LightingText( + "stateInlet", + client, + "Inlet", + width = 70.dp, + height = 25.dp, + borderWidth = 5.dp + ) + }, 500, 150) + PositionedBox(@Composable { + LightingText( + "stateHeat", + client, + "Heat", + width = 70.dp, + height = 25.dp, + borderWidth = 5.dp + ) + }, 500, 200) + PositionedBox(@Composable { + LightingText( + "stateOutlet", + client, + "Outlet", + width = 70.dp, + height = 25.dp, + borderWidth = 5.dp + ) + }, 500, 250) +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/example/WATER_TANK/WATER_TANK.xml b/code/cat_visual/src/main/kotlin/example/WATER_TANK/WATER_TANK.xml new file mode 100644 index 000000000..bba095874 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/example/WATER_TANK/WATER_TANK.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/example/WATER_TANK/WATER_TANK_CONF.xml b/code/cat_visual/src/main/kotlin/example/WATER_TANK/WATER_TANK_CONF.xml new file mode 100644 index 000000000..b7461d6df --- /dev/null +++ b/code/cat_visual/src/main/kotlin/example/WATER_TANK/WATER_TANK_CONF.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/example/WATER_TANK/WATER_TANK_PLAIN.xml b/code/cat_visual/src/main/kotlin/example/WATER_TANK/WATER_TANK_PLAIN.xml new file mode 100644 index 000000000..f59dbb5dc --- /dev/null +++ b/code/cat_visual/src/main/kotlin/example/WATER_TANK/WATER_TANK_PLAIN.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/lib/elements/figures/elements.kt b/code/cat_visual/src/main/kotlin/lib/elements/figures/elements.kt new file mode 100644 index 000000000..d23a47a84 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/lib/elements/figures/elements.kt @@ -0,0 +1,212 @@ +package lib.elements + +import androidx.compose.animation.core.AnimationSpec +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.CutCornerShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + + +@Composable +fun Rectangle(color: Color, width: Dp, height: Dp) { + Box( + modifier = Modifier.background(color = color) + .width(width) + .height(height) + ) { + } +} + +@Composable +fun Rectangle(color: Color, size: Dp) { + Box( + modifier = Modifier.background(color = color) + .size(size) + ) { + } +} + +@Composable +fun Round(color: Color, width: Dp, height: Dp) { + Box( + modifier = Modifier.background(color = color) + .clip(CircleShape) + .width(width) + .height(height) + ) { + } +} + +@Composable +fun Round(color: Color, size: Dp) { + Box( + modifier = Modifier.background(color = color) + .clip(CircleShape) + .size(size) + ) { + } +} + +@Composable +fun Round(modifier: Modifier, width: Dp, height: Dp) { + Box( + modifier = Modifier + .clip(CircleShape) + .width(width) + .height(height) + .then(modifier) + ) { + } +} + +@Composable +fun RoundedRectangle(color: Color, width: Dp, height: Dp, radius: Dp) { + Box( + modifier = Modifier.background(color = color) + .clip(RoundedCornerShape(radius)) + .width(width) + .height(height) + ) { + } +} + +@Composable +fun RoundedRectangle(color: Color, size: Dp, radius: Dp) { + Box( + modifier = Modifier.background(color = color) + .clip(RoundedCornerShape(radius)) + .size(size) + ) { + } +} + + +@Composable +fun RoundedRectangle(modifier: Modifier, width: Dp, height: Dp, radius: Dp) { + Box( + modifier = modifier + .clip(RoundedCornerShape(radius)) + .width(width) + .height(height) + ) { + } +} + +@Composable +fun CutRectangle(color: Color, width: Dp, height: Dp, radius: Dp) { + Figure(shape = CutCornerShape(radius), + modifier = Modifier.background(color = color) + .width(width) + .height(height) + ) +} + +@Composable +fun CutRectangle(color: Color, size: Dp, radius: Dp) { + Figure( + shape = CutCornerShape(radius), + modifier = Modifier.background(color = color) + .clip(CutCornerShape(radius)) + .size(size) + ) +} + + +@Composable +fun Figure(shape: Shape = RectangleShape, modifier: Modifier = Modifier) { + Box(modifier = modifier.clip(shape)){ + } +} + +@Composable +fun OneDAnimatedComposable( + content: @Composable () -> Unit, + isAtTheEnd: Boolean, + targetValue: Float, + horizontally: Boolean = true, + animationSpec: AnimationSpec = tween(durationMillis = 1000), +) { + val animatedValue by animateFloatAsState( + targetValue = if (isAtTheEnd) targetValue else 0f, + animationSpec = animationSpec + ) + + var modifier:Modifier = Modifier; + if (horizontally) { + modifier = modifier.offset(x = animatedValue.dp) + } else { + modifier = modifier.offset(y = animatedValue.dp) + } + Box( + modifier = modifier + ) { + content() + } +} + +@Composable +fun Pipe( + color: Color, + pipeWidth: Dp, + pipeHeight: Dp, + modifier: Modifier = Modifier +) { + Box( + modifier = modifier + .width(pipeWidth) + .height(pipeHeight) + .background(brush = Brush.verticalGradient(listOf(color, color.copy(alpha = 0.2f), color))) + ){} +} + +@Composable +fun PipeConnector( + color: Color, + connectorWidth: Dp, + connectorHeight: Dp, + modifier: Modifier = Modifier +) { + Box(modifier = modifier) { + Box( + modifier = Modifier + .offset() + .width(connectorWidth) + .height(connectorHeight) + .background( + brush = Brush.verticalGradient(listOf(color, color.copy(alpha = 0.2f), color)), + shape = RoundedCornerShape(0.dp, connectorHeight, connectorHeight, 0.dp) + ) + ) {} + val rotatedBoxOffset = connectorWidth / 2 - connectorHeight /2 + Box( + modifier = Modifier + .offset(x = rotatedBoxOffset, y = rotatedBoxOffset) + .rotate(-90f) + .width(connectorWidth) + .height(connectorHeight) + .background( + brush = Brush.verticalGradient(listOf(color, color.copy(alpha = 0.2f), color)), + shape = RoundedCornerShape(0.dp, connectorHeight, connectorHeight, 0.dp) + ) + ) {} + } + +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/lib/elements/getters/elements.kt b/code/cat_visual/src/main/kotlin/lib/elements/getters/elements.kt new file mode 100644 index 000000000..3be2f0045 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/lib/elements/getters/elements.kt @@ -0,0 +1,221 @@ +package lib.elements.getters + +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.font.SystemFontFamily +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.zIndex +import connection.AbstractClient +import connection.ConnectionFieldRegistry +import connection.field.ConnectionField +import connection.field.TYPE_ID +import connection.provider.ConnectionProvider +import lib.elements.* + +@Composable +fun Bulb(name: String, client: AbstractClient, + size: Dp = 60.dp, borderWidth:Dp = 10.dp, + modifier: Modifier = Modifier + ) { + + val field = client.getField(name) as ConnectionField + + var checkedStateBulb = remember { field } + Round( + modifier.background(brush = if (checkedStateBulb.contentState.value) LIGHT_RADIAL_BRUSH else DARK_RADIAL_BRUSH) + .border(width = borderWidth, brush = METALLIC_BRUSH, shape = CircleShape), + size, + size + ) +} + + +@Composable +fun LightingText(name: String, client: AbstractClient, + text:String, + width: Dp = 60.dp, height: Dp = 60.dp, borderWidth:Dp = 10.dp, + modifier: Modifier = Modifier, lightBrush: Brush = LIGHT_RADIAL_BRUSH + ) { + + val field = client.getField(name) as ConnectionField + + var checkedStateBulb = remember { field } + + Box( + modifier = modifier.background(brush = if (checkedStateBulb.contentState.value) lightBrush else DARK_RADIAL_BRUSH, shape = RoundedCornerShape(height / 4)) + .border(width = borderWidth, brush = METALLIC_BRUSH, shape = RoundedCornerShape(height / 4)) + .widthIn(min = width) + .heightIn(min = height) + .clip(RoundedCornerShape(height / 4)), + ) { + Text(text, modifier = Modifier.align(Alignment.Center)) + } +} + +@Composable +fun TextBox( + name: String, + client: AbstractClient, + modifier: Modifier = Modifier.focusable(true) +) { + val field = client.getField(name) + + var checkedState = remember { field } + Box( + modifier = modifier.then(Modifier.fillMaxWidth().fillMaxHeight()) + ) { + Text(checkedState!!.contentState.value.toString()) + } +} + +@Composable +fun Indicator( + name: String, client: AbstractClient, + minIndicatorValue: Int, maxIndicatorValue: Int, size: Dp, + indicatorThickness: Dp = 28.dp, + animationDuration: Int = 1000, + animationDelay: Int = 0 +) { + val field = client.getField(name) as ConnectionField + + var checkedState = remember { field } + Box( + contentAlignment = Alignment.Center, + modifier = Modifier.wrapContentSize().padding(indicatorThickness) + ) { + Canvas( + modifier = Modifier + .height(size + indicatorThickness) + .width(size + indicatorThickness) + ) { + drawArc( + color = Color.LightGray, + startAngle = 0f, + sweepAngle = -180f, + useCenter = false, + style = Stroke(width = indicatorThickness.toPx(), cap = StrokeCap.Round) + ) + + var sweepAngle = 0f; + if (checkedState.contentState.value.toInt() != 0) { + sweepAngle = (checkedState.contentState.value.toInt() / (maxIndicatorValue - minIndicatorValue).toFloat()) * 180f + } + + // Foreground circle + drawArc( + color = Color.Red, + startAngle = 180f, + sweepAngle = sweepAngle, + useCenter = false, + style = Stroke(indicatorThickness.toPx(), cap = StrokeCap.Round) + ) + + + } + Text( + text = checkedState.contentState.value.toString(), + color = Color.Black, + style = MaterialTheme.typography.body1, + textAlign = TextAlign.Center, + modifier = Modifier + .align(Alignment.Center), + fontSize = 30.sp + ) + } +} + + +@Composable +fun VerticalIndicator( + name: String, client: AbstractClient, + temperature: Float = 0f, + maxTemperature: Float, + minTemperature: Float, + color: Color, + step: Float, + modifier: Modifier = Modifier, + trackHeight: Dp = 400.dp, + indicatorWidth: Dp = 48.dp, + legendFontSize: TextUnit = 12.sp, + legendFontColor: Color = Color.Black, + font: SystemFontFamily = FontFamily.Default, + showCurrentState: Boolean = false +) { + val field = client.getField(name) as ConnectionField + var checkedState = remember { field } + + val stepCount = ((maxTemperature - minTemperature) / step).toInt() + val temperatureRange = maxTemperature - minTemperature + + Box( + modifier = modifier + .width(indicatorWidth) + .height(trackHeight) + .background(Color.LightGray) + .border(BorderStroke(3.dp, METALLIC_BRUSH), RectangleShape) + ) { + val temperatureOffset = (checkedState.contentState.value.toFloat() - minTemperature) / temperatureRange + val indicatorHeight = (temperatureOffset * trackHeight.value).dp + + + Box( + modifier = Modifier + .fillMaxWidth() + .height(indicatorHeight) + .background(color) + .align(Alignment.BottomCenter) + ) { + + if (showCurrentState) { + Text( + text = "${checkedState.contentState.value}", + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + color = legendFontColor, + modifier = Modifier.offset(y = trackHeight) + ) + } + } + + } + for (i in (minTemperature.toInt()..maxTemperature.toInt()).reversed() step step.toInt()) { + val halfLegendHeight = legendFontSize.value.dp / 2f + val labelOffset = ((i - minTemperature) / temperatureRange).coerceIn(0f, 1f) + val labelTop = (((1f - labelOffset) * (trackHeight.value)) - halfLegendHeight.value).coerceIn(0f, trackHeight.value).dp + Box(modifier = Modifier.width(indicatorWidth * 2)) { + Text( + modifier = Modifier.offset(x = indicatorWidth, y = labelTop), + text = "$i", + fontSize = legendFontSize, + fontWeight = FontWeight.Light, + fontFamily = font, + color = legendFontColor, + softWrap = false, + lineHeight = legendFontSize + ) + } + } + +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/lib/elements/setters/elements.kt b/code/cat_visual/src/main/kotlin/lib/elements/setters/elements.kt new file mode 100644 index 000000000..a13e073dd --- /dev/null +++ b/code/cat_visual/src/main/kotlin/lib/elements/setters/elements.kt @@ -0,0 +1,549 @@ +package lib.elements.setters + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.* +import androidx.compose.foundation.gestures.* +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Done +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.input.pointer.consumeAllChanges +import androidx.compose.ui.input.pointer.consumePositionChange +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.input.pointer.positionChange +import androidx.compose.ui.layout.boundsInWindow +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.* +import androidx.compose.ui.zIndex +import connection.AbstractClient +import connection.ConnectionFieldRegistry +import connection.field.* +import connection.provider.ConnectionProvider +import lib.elements.METALLIC_BRUSH +import java.lang.UnsupportedOperationException +import kotlin.concurrent.thread +import kotlin.math.PI +import kotlin.math.atan2 +import kotlin.math.roundToInt + +@Composable +fun CustomButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + buttonColors: ButtonColors = ButtonDefaults.buttonColors(), + textColor: Color = Color.Black, + backgroundColor: Color = Color.White, + borderColor: Color = Color.Transparent, + borderWidth: Int = 0, + cornerRadius: Int = 0 +) { + Button( + onClick = onClick, + modifier = modifier, + colors = buttonColors, + shape = RoundedCornerShape(cornerRadius.dp), + border = BorderStroke(width = borderWidth.dp, color = borderColor), + contentPadding = PaddingValues(16.dp), + elevation = ButtonDefaults.elevation(defaultElevation = 0.dp, pressedElevation = 2.dp) + ) { + Text( + text = text, + color = textColor, + modifier = Modifier.padding(4.dp) + ) + } +} + +@Composable +fun Toggle( + name: String, client: AbstractClient, + modifier: Modifier = Modifier, + onCheckedChange: (Boolean) -> Unit = {} +) { + val toggleField = client.getField(name) as ConnectionField + + val checkedStateToggle = remember { toggleField } + Switch( + modifier = modifier.size(24.dp), + checked = checkedStateToggle.contentState.value, + onCheckedChange = { + checkedStateToggle.setValue(it) + client.sendValue(name) + onCheckedChange(it) + } + ) +} + +@Composable +fun Checkbox( + name: String, client: AbstractClient, + modifier: Modifier = Modifier, + onCheckedChange: (Boolean) -> Unit = {}, + checkboxColor: Color = MaterialTheme.colors.primary, + checkmarkColor: Color = Color.White, + disabledColor: Color = MaterialTheme.colors.onSurface.copy(alpha = 0.38f), + contentDescription: String? = null +) { + val checkboxField = client.getField(name) as ConnectionField + + val checkedStateCheckbox = remember { checkboxField } + + Box( + modifier = modifier + .clickable( + onClick = { + val changedValue = !checkedStateCheckbox.contentState.value + checkedStateCheckbox.setValue(changedValue) + client.sendValue(name) + onCheckedChange(changedValue) + }) + .size(24.dp), + contentAlignment = Alignment.Center + ) { + val backgroundColor = if (checkedStateCheckbox.contentState.value) checkboxColor else disabledColor + Box( + modifier = Modifier + .size(20.dp) + .background(color = backgroundColor) + .border(width = 2.dp, brush = METALLIC_BRUSH, shape = RectangleShape), + contentAlignment = Alignment.Center + ) { + if (checkedStateCheckbox.contentState.value) { + Icon( + imageVector = Icons.Default.Check, + contentDescription = contentDescription, + tint = checkmarkColor, + modifier = Modifier.size(12.dp) + ) + } + } + } +} + +@Composable +fun ListBox( + name: String, client: AbstractClient, + modifier: Modifier = Modifier, + items: List, + onItemSelected: (String) -> Unit = {}, + dropdownMaxHeight: Dp = 240.dp, + content: @Composable (String) -> Unit +) { + val comboBoxField = client.getField(name) as ConnectionField + + val checkedStateComboBox = remember { comboBoxField } + var expanded by remember { mutableStateOf(false) } + var dropdownHeight by remember { mutableStateOf(0.dp) } + val density = LocalDensity.current + val dropdownHeightPx = with(density) { dropdownHeight.toPx() } + var selectedItem: String by remember { mutableStateOf("") } + var textfieldSize by remember { mutableStateOf(0) } + val interactionSource = remember { + MutableInteractionSource() + } + + Column( + modifier = modifier.wrapContentSize().clickable( + interactionSource = interactionSource, + indication = null, + onClick = { + expanded = false + } + ), + horizontalAlignment = Alignment.Start + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .background(color = Color.White, shape = MaterialTheme.shapes.medium) + .padding(horizontal = 16.dp, vertical = 12.dp) + ) { + Row(verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.onSizeChanged { + textfieldSize = it.width +// print(textfieldSize) + }) { + + Box(modifier = Modifier.weight(1f)) { + content(selectedItem) + } + Icon(imageVector = Icons.Default.ArrowDropDown, + contentDescription = null, + Modifier.size(24.dp).alignByBaseline().clickable { expanded = !expanded }) + + } + } + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + modifier = Modifier + .heightIn(max = dropdownMaxHeight) + .width(with(LocalDensity.current) { textfieldSize.toDp() }) + .onSizeChanged { dropdownHeight = it.height.dp } + ) { + items.forEach { item -> + DropdownMenuItem( + onClick = { + checkedStateComboBox.setValue(item) + client.sendValue(name) + onItemSelected(item) + selectedItem = item + expanded = false + }, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = 48.dp) + ) { + content(item) + } + } + } + } +} + + +@Composable +fun ComboBox( + name: String, client: AbstractClient, + modifier: Modifier = Modifier, + items: List, + onItemSelected: (String) -> Unit = {}, + dropdownMaxHeight: Dp = 240.dp, + content: @Composable (String) -> Unit +) { + val comboBoxField = client.getField(name) as ConnectionField + + val checkedStateComboBox = remember { comboBoxField } + var expanded by remember { mutableStateOf(false) } + var dropdownHeight by remember { mutableStateOf(0.dp) } + var selectedItem by remember { mutableStateOf("") } + var textfieldSize by remember { mutableStateOf(0) } + val scrollState = rememberScrollState() + + Column( + modifier = modifier.wrapContentSize(), + horizontalAlignment = Alignment.Start + ) { + + Box(modifier = Modifier.fillMaxWidth()) { + OutlinedTextField( + value = selectedItem, + onValueChange = { + selectedItem = it + expanded = true + }, + modifier = Modifier.fillMaxWidth(), + label = { Text("Select an item") }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Text, + imeAction = ImeAction.Done + ), + trailingIcon = { + Icon( + Icons.Default.ArrowDropDown, + "Drop Down", + Modifier.size(24.dp).clickable { expanded = !expanded } + ) + } + ) + } + AnimatedVisibility(visible = expanded) { + Card( + modifier = Modifier + .padding(horizontal = 5.dp) + .fillMaxWidth(), + elevation = 15.dp + ) { + var currItems: List = items.sorted() + if (selectedItem.isNotEmpty()) { + currItems = items.filter { it.lowercase().contains(selectedItem.lowercase()) } + } + LazyColumn { + items(currItems){ item -> + Box( + modifier = Modifier.clickable { + checkedStateComboBox.setValue(item) + client.sendValue(name) + onItemSelected(item) + selectedItem = item + expanded = false + } + .fillMaxWidth() + .heightIn(min = 48.dp) + ) { + content(item) + } + } + } + + } + } + } +} + +@Composable +fun InputTextBox( + name: String, client: AbstractClient, + modifier: Modifier = Modifier, + onItemChanged: (String) -> Unit = {}, + hintText: String = "Write something" +) { + val textBoxField = client.getField(name) as ConnectionField + val checkedStateTextBox = remember { textBoxField } + var selectedItem: String by remember { mutableStateOf("") } + Box(modifier = Modifier.fillMaxWidth()) { + OutlinedTextField( + value = selectedItem, + onValueChange = { + selectedItem = it + }, + modifier = modifier.fillMaxWidth(), + label = { Text(hintText) }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Text, + imeAction = ImeAction.Done + ), + trailingIcon = { + Icon( + Icons.Default.Check, + "Drop Down", + Modifier.size(24.dp).clickable { + checkedStateTextBox.setValue(selectedItem) + client.sendValue(name) + onItemChanged(checkedStateTextBox.getValue()) + } + ) + } + ) + } +} + + + +@Composable +fun HorizontalTracker( + name: String, client: AbstractClient, + range: ClosedFloatingPointRange, + initialValue: Float, + onValueSelected: (Float) -> Unit = {}, + modifier: Modifier = Modifier, + thumbColor: Color = Color.Magenta, + trackColor: Color = Color.LightGray, + borderWidth: Dp = 3.dp, + thumbSize: Dp = 24.dp, + trackWidth: Dp = 100.dp, + trackHeight: Dp = 24.dp, + legend: Boolean = true, + steps: Int = 0, + tickSize: Dp = 1.dp, + legendFontSize: TextUnit = 10.sp +) { + val trackerField = client.getField(name) as ConnectionField + val checkedStateTracker = remember { trackerField } + + var offsetX by remember { mutableStateOf(0f) } + var position by remember { mutableStateOf(getThumbPosition(initialValue, range, thumbSize, trackWidth)) } + var selectedValue by remember { mutableStateOf(initialValue) } + + Box(modifier = modifier.padding(thumbSize / 2)) { + Spacer( + modifier = Modifier + .width(trackWidth) + .height(trackHeight) + .background(trackColor) + .zIndex(1f) + .border(BorderStroke(borderWidth, METALLIC_BRUSH)) + ) + + Box( + modifier = Modifier + .offset(x = position) + .size(thumbSize) + .background(thumbColor, CircleShape) + .zIndex(2f) + .pointerInput(Unit) { + detectDragGestures( + onDragStart = { + }, + onDragEnd = { + val newValue = ((position.value + thumbSize.value / 2) / trackWidth.value) * (range.endInclusive - range.start) + selectedValue = newValue.coerceIn(range) + checkedStateTracker.setValue(selectedValue) + client.sendValue(name) + onValueSelected(selectedValue) + }, + onDragCancel = { + }, + onDrag = { change, _ -> + offsetX = change.positionChange().x + position = max(0.dp - thumbSize / 2, min(position + offsetX.toDp(), trackWidth - thumbSize / 2)) + change.consumePositionChange() + } + ) + } + ) + + if (legend && steps > 0) { + val tickPositions = + (0..steps).map { Pair(range.start + it.toFloat() / steps * (range.endInclusive - range.start), it) } + tickPositions.forEach { position -> + val tickPosition = getTickPosition(position.first, range, thumbSize) + Column(modifier = Modifier.offset(x = tickPosition.dp).zIndex(0f)) { + Spacer( + modifier = Modifier + .height(trackHeight + tickSize) + .width(1.dp) + .background(Color.Black) + + ) + Text(position.second.toString(), modifier = Modifier.align(Alignment.CenterHorizontally), fontSize = legendFontSize) + } + } + } + } +} + +fun getThumbPosition(value: Float, range: ClosedFloatingPointRange, thumbWidth: Dp, trackWidth: Dp): Dp { + val normalizedValue = (value - range.start) / (range.endInclusive - range.start) + return trackWidth * normalizedValue + thumbWidth / 2 +} + +fun getTickPosition(value: Float, range: ClosedFloatingPointRange, thumbWidth: Dp): Float { + val normalizedValue = (value - range.start) / (range.endInclusive - range.start) + val availableWidth = (range.endInclusive - range.start) + return availableWidth * normalizedValue +} + + +@Composable +fun RoundKnob( + name: String, client: AbstractClient, + range: IntRange, + value: Float, + onValueSelected: (Number) -> Unit = {}, + knobSize: Dp = 48.dp, + strokeWidth: Dp = 4.dp, + knobColor: Color = MaterialTheme.colors.primary, + legendColor: Color = MaterialTheme.colors.onBackground +) { + val knobField = client.getField(name) as ConnectionField + val checkedStateKnob = remember { knobField } + + var rotationAngle by remember { mutableStateOf(calculateRotationAngle(value, range))} + var currValue by remember { mutableStateOf(value) } + var offsetX by remember { mutableStateOf(0f) } + var offsetY by remember { mutableStateOf(0f) } + var centerX by remember { mutableStateOf(0f) } + var centerY by remember { mutableStateOf(0f) } + Box( + contentAlignment = Alignment.Center, + modifier = Modifier.size(knobSize) + ) { + Box(modifier = Modifier.pointerInput(Unit) { + detectDragGestures( + onDragStart = { + }, + onDragEnd = { + var angle = atan2(centerY - offsetY, centerX - offsetX) * (180f / PI).toFloat() - 90 + if (angle < -180) { + angle = (angle + 360) % 360 + } + rotationAngle = (angle).coerceIn(-150f, 150f) + currValue = calculateValue(rotationAngle, range) + + checkedStateKnob.setValue(currValue) + client.sendValue(name) + onValueSelected(currValue) + }, + onDrag = { change, dragAmount -> + offsetX = change.position.x + offsetY = change.position.y + var angle = atan2(centerY - offsetY, centerX - offsetX) * (180f / PI).toFloat() - 90 + if (angle < -180) { + angle = (angle + 360) % 360 + } + rotationAngle = (angle).coerceIn(-150f , 150f) + currValue = calculateValue(rotationAngle, range) + change.consumePositionChange() + } + ) + }) { + Canvas(modifier = Modifier.matchParentSize().onGloballyPositioned { + val windowBounds = it.boundsInWindow() + centerX = windowBounds.size.width / 2f + centerY = windowBounds.size.height / 2f + }) { + drawCircle( + brush = METALLIC_BRUSH, + radius = size.minDimension / 2, + style = Stroke(width = strokeWidth.toPx()) + ) + } + + Canvas( + modifier = Modifier + .size(knobSize) + .rotate(rotationAngle) + ) { + drawLine( + color = knobColor, + start = Offset(x = size.width / 2, y = 0f), + end = Offset(x = size.width / 2, y = size.height / 2), + strokeWidth = strokeWidth.toPx(), + cap = StrokeCap.Round + ) + } + } + + Text( + text = currValue.toString(), + color = legendColor, + style = MaterialTheme.typography.body1, + textAlign = TextAlign.Center, + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = 8.dp) + ) + } +} + +private fun calculateRotationAngle(value: Float, range: IntRange):Float { + val ratio = (value - range.start) / (range.endInclusive - range.start) + return ratio * 300 - 150 +} + +private fun calculateValue(rotationAngle: Float, range: IntRange): Float { + val ratio = (rotationAngle + 150) / 300 + val value = ratio * (range.endInclusive - range.start) + range.start + return value +} diff --git a/code/cat_visual/src/main/kotlin/lib/visual/Positioning.kt b/code/cat_visual/src/main/kotlin/lib/visual/Positioning.kt new file mode 100644 index 000000000..7f8ae0082 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/lib/visual/Positioning.kt @@ -0,0 +1,32 @@ +package lib.visual + +import androidx.compose.foundation.layout.* +import androidx.compose.runtime.Composable + +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex + +@Composable +fun PositionedBox(children: @Composable () -> Unit, x: Int = 0, y:Int = 0, zIndex: Float = 1f) { + Box(modifier = Modifier. + zIndex(zIndex) + .offset {IntOffset(x, y)} + .wrapContentSize()) { + children() + } +} + +@Composable +fun PositionedBox(children: @Composable () -> Unit, x: Float = 0f, y:Float = 0f, zIndex: Float = 1f) { + Box(modifier = Modifier. + zIndex(zIndex) + .offset(x.dp, y.dp) + .wrapContentSize()) { + children() + } +} + + diff --git a/code/cat_visual/src/main/kotlin/lib/visual/colors.kt b/code/cat_visual/src/main/kotlin/lib/visual/colors.kt new file mode 100644 index 000000000..f67d03cf3 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/lib/visual/colors.kt @@ -0,0 +1,51 @@ +package lib.elements + +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color + + + + +val LIGHT_RADIAL_BRUSH = Brush.radialGradient( + colors = listOf( + Color.Yellow, + Color(0xFFFFA500) + ) +) + +val GREEN_RADIAL_BRUSH = Brush.radialGradient( + colors = listOf( + Color.Gray, + Color.Yellow + ) +) + +val DARK_RADIAL_BRUSH = Brush.radialGradient( + colors = listOf( + Color.DarkGray, + Color.Gray + ) +) + +val LIGHT_HORIZONTAL_BRUSH = Brush.horizontalGradient( + colors = listOf( + Color.Yellow, + Color(0xFFA500FF) + ) +) + +val DARK_HORIZONTAL_BRUSH = Brush.horizontalGradient( + colors = listOf( + Color.Black, + Color.Gray + ) +) + +val METALLIC_BRUSH = Brush.linearGradient( + colors = listOf( + Color.White, + Color.Gray, + Color.DarkGray + ) +) + diff --git a/code/cat_visual/src/main/kotlin/serializer/serialization.kt b/code/cat_visual/src/main/kotlin/serializer/serialization.kt new file mode 100644 index 000000000..9b4923cea --- /dev/null +++ b/code/cat_visual/src/main/kotlin/serializer/serialization.kt @@ -0,0 +1,155 @@ +package serializer + +import kotlinx.serialization.Serializable +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.serializer +import nl.adaptivity.xmlutil.XmlDeclMode +import nl.adaptivity.xmlutil.serialization.XML +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlSerialName + +@Serializable +@XmlSerialName("Mapping", "", "") +data class PlainMapping( + val inputs: PlainInputs, + val outputs: PlainOutputs, +) + +@Serializable +@XmlSerialName("Inputs", "", "") +data class PlainInputs( + val inputs: List +) + +@Serializable +@XmlSerialName("Input", "", "") +data class PlainInput( + val name: String, + val type: String, + val host: String, + val port: Int +) + +@Serializable +@XmlSerialName("Outputs", "", "") +data class PlainOutputs( + val outputs: List + +) + +@Serializable +@XmlSerialName("Output", "", "") +data class PlainOutput( + val name: String, + val type: String, + val host: String, + val port: Int +) + + +fun getPlainMapping(modelText: String): PlainMapping { + val module = SerializersModule {} + val xml = XML(module) { + indentString = " " + xmlDeclMode = XmlDeclMode.Minimal + autoPolymorphic = true + } + + val serializer = serializer() + return xml.decodeFromString(serializer, modelText) +} + +@Serializable +@XmlSerialName("Mapping", "", "") +data class Mapping( + val id: String, + val inputs: Inputs, + val outputs: Outputs, +) + +@Serializable +@XmlSerialName("Inputs", "", "") +data class Inputs( + val inputs: List +) + +@Serializable +@XmlSerialName("Input", "", "") +data class Input( + val name: String, + val type: String +) + +@Serializable +@XmlSerialName("Outputs", "", "") +data class Outputs( + val outputs: List +) + +@Serializable +@XmlSerialName("Output", "", "") +data class Output( + val name: String, + val type: String +) + + +fun getMapping(modelText: String): Mapping { + val module = SerializersModule {} + val xml = XML(module) { + indentString = " " + xmlDeclMode = XmlDeclMode.Minimal + autoPolymorphic = true + } + + val serializer = serializer() + return xml.decodeFromString(serializer, modelText) +} + + +@Serializable +@XmlSerialName("Conf", "", "") +data class Conf( + val inputs: ConfInputs, + val outputs: ConfOutputs, +) + +@Serializable +@XmlSerialName("Inputs", "", "") +data class ConfInputs( + val inputs: List +) +@Serializable +@XmlSerialName("Input", "", "") +data class ConfInput( + val type: String, + val host: String, + val port: Int +) + +@Serializable +@XmlSerialName("Outputs", "", "") +data class ConfOutputs( + val outputs: List +) + +@Serializable +@XmlSerialName("Output", "", "") +data class ConfOutput( + val type: String, + val host: String, + val port: Int +) + + +fun getConf(modelText: String): Conf { + val module = SerializersModule {} + val xml = XML(module) { + indentString = " " + xmlDeclMode = XmlDeclMode.Minimal + autoPolymorphic = true + } + + val serializer = serializer() + return xml.decodeFromString(serializer, modelText) +} \ No newline at end of file diff --git a/code/cat_visual/src/test/kotlin/MockBackend.kt b/code/cat_visual/src/test/kotlin/MockBackend.kt new file mode 100644 index 000000000..079444c19 --- /dev/null +++ b/code/cat_visual/src/test/kotlin/MockBackend.kt @@ -0,0 +1,13 @@ +import connection.AbstractClient +import connection.ConnectionFieldRegistry +import connection.field.ConnectionField +import connection.field.TYPE_ID +import serializer.Mapping +import serializer.PlainMapping +import serializer.getMapping +import serializer.getPlainMapping +import java.io.File + +class FieldWithCallback(val field: ConnectionField, val callback: (V) -> Unit) + + diff --git a/code/cat_visual/src/test/kotlin/WaterTankTest.kt b/code/cat_visual/src/test/kotlin/WaterTankTest.kt new file mode 100644 index 000000000..a3559bc1e --- /dev/null +++ b/code/cat_visual/src/test/kotlin/WaterTankTest.kt @@ -0,0 +1,90 @@ +import connection.field.ConnectionField +import kotlin.concurrent.thread + +val client = buildMappingClient("src/main/kotlin/example/WATER_TANK/WATER_TANK.xml", "src/main/kotlin/example/WATER_TANK/WATER_TANK_CONF.xml", mode = "") + + +val TANK_VOLUME = 100 +val MAX_TEMP = 100 + +var isInlet: FieldWithCallback = FieldWithCallback(client.getField("stateInlet") as ConnectionField, + {client.sendValue("stateInlet")}) +var isHeat: FieldWithCallback = FieldWithCallback(client.getField("stateHeat") as ConnectionField, + {client.sendValue("stateHeat")}) +var isOutlet: FieldWithCallback = FieldWithCallback(client.getField("stateOutlet") as ConnectionField, + {client.sendValue("stateOutlet")}) +var lamp1: FieldWithCallback = FieldWithCallback(client.getField("lampIndicator1") as ConnectionField, + {client.sendValue("lampIndicator1")}) +var lamp2: FieldWithCallback = FieldWithCallback(client.getField("lampIndicator2") as ConnectionField, + {client.sendValue("lampIndicator2")}) +var tankIndicator: FieldWithCallback = FieldWithCallback(client.getField("tankIndicator") as ConnectionField, + {client.sendValue("tankIndicator")}) +var outputIndicator: FieldWithCallback = FieldWithCallback(client.getField("outputIndicator") as ConnectionField, + {client.sendValue("outputIndicator")}) +var knob1: FieldWithCallback = FieldWithCallback(client.getField("knob1") as ConnectionField, { + runUpdate() +}) +var knob2: FieldWithCallback = FieldWithCallback(client.getField("knob2") as ConnectionField, { + runUpdate() +}) + + +fun runSimulation(){ + client.retrieveValues(mapOf(Pair("knob1", {knob1.callback(0)}), Pair("knob2", {knob2.callback(0)})), false) +} + +fun runUpdate() { + if (knob1.field.getValue() == 0) { + return + } + if (isInlet.field.getValue() || isHeat.field.getValue() || isOutlet.field.getValue()) { + return + } + isInlet.field.setValue(true) + isInlet.callback(true) + lamp1.field.setValue(true) + lamp1.callback(true) + var currTankState = 0 + while (currTankState < TANK_VOLUME) { + currTankState += knob1.field.getValue() / 2 + tankIndicator.field.setValue(currTankState.toFloat()) + tankIndicator.callback(currTankState.toFloat()) + Thread.sleep( 500) + } + isInlet.field.setValue(false) + isInlet.callback(false) + lamp1.field.setValue(false) + lamp1.callback(false) + isHeat.field.setValue(true) + isHeat.callback(true) + var currTemp = knob2.field.getValue() + while (currTemp < MAX_TEMP) { + currTemp += 10 + outputIndicator.field.setValue(currTemp.toFloat()) + outputIndicator.callback(currTemp.toFloat()) + Thread.sleep(500) + } + isHeat.field.setValue(false) + isHeat.callback(false) + isOutlet.field.setValue(true) + isOutlet.callback(true) + lamp2.field.setValue(true) + lamp2.callback(true) + currTankState = TANK_VOLUME + while (currTankState > 0) { + currTankState -= knob1.field.getValue() / 2 + tankIndicator.field.setValue(currTankState.toFloat()) + tankIndicator.callback(currTankState.toFloat()) + Thread.sleep( 500) + } + isOutlet.field.setValue(false) + isOutlet.callback(false) + lamp2.field.setValue(false) + lamp2.callback(false) + outputIndicator.field.setValue(0f) + outputIndicator.callback(0f) +} + +fun main() { + runSimulation() +} \ No newline at end of file diff --git a/code/debugger/build.gradle.kts b/code/debugger/build.gradle.kts index 7e84b181b..da9eedb79 100644 --- a/code/debugger/build.gradle.kts +++ b/code/debugger/build.gradle.kts @@ -19,18 +19,11 @@ dependencies { compileOnly(project(":code:library")) compileOnly(project(":code:language")) compileOnly(project(":code:platform")) + compileOnly(project(":code:scenes")) compileOnly(project(":code:richediting")) - -// implementation(compose.desktop.currentOs) - - mpsImplementation(project(":code:library", "mps")) - mpsImplementation(project(":code:language", "mps")) - mpsImplementation(project(":code:platform", "mps")) - mpsImplementation(project(":code:richediting", "mps")) } mps { - buildScriptName.set("fbme_debugger") moduleName.set("org.fbme.debugger.lib") moduleDependency(project(":code:library")) diff --git a/code/debugger/buildsolution/models/org.fbme.debugger.build.mps b/code/debugger/buildsolution/models/org.fbme.debugger.build.mps deleted file mode 100644 index be029e7a5..000000000 --- a/code/debugger/buildsolution/models/org.fbme.debugger.build.mps +++ /dev/null @@ -1,300 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/code/debugger/buildsolution/org.fbme.debugger.build.msd b/code/debugger/buildsolution/org.fbme.debugger.build.msd deleted file mode 100644 index a8400ec8c..000000000 --- a/code/debugger/buildsolution/org.fbme.debugger.build.msd +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - 422c2909-59d6-41a9-b318-40e6256b250f(jetbrains.mps.ide.build) - ab2b933c-4cca-47c1-aeca-d2ac0bca990d(org.fbme.language.build) - c44ff7f6-aa34-4dc2-a9b3-93cc101df3d4(org.fbme.richediting.build) - d76643a8-b8a1-4b1a-9500-66247bf6bc35(org.fbme.ide.build.plugins) - - - - - - - - - - - - - - - - - - diff --git a/code/debugger/solutions/org.fbme.debugger/models/org.fbme.debugger.plugin.mps b/code/debugger/solutions/org.fbme.debugger/models/org.fbme.debugger.plugin.mps deleted file mode 100644 index 6fe8b6fec..000000000 --- a/code/debugger/solutions/org.fbme.debugger/models/org.fbme.debugger.plugin.mps +++ /dev/null @@ -1,9374 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/code/debugger/solutions/org.fbme.debugger/org.fbme.debugger.msd b/code/debugger/solutions/org.fbme.debugger/org.fbme.debugger.msd deleted file mode 100644 index 9587e2e23..000000000 --- a/code/debugger/solutions/org.fbme.debugger/org.fbme.debugger.msd +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - 6354ebe7-c22a-4a0f-ac54-50b52ab9b065(JDK) - 742f6602-5a2f-4313-aa6e-ae1cd4ffdc61(MPS.Platform) - 498d89d2-c2e9-11e2-ad49-6cf049e62fe5(MPS.IDEA) - 8865b7a8-5271-43d3-884c-6fd1d9cfdd34(MPS.OpenAPI) - 6594f340-4d73-4027-b7d3-c6ca2e70a53b(org.fbme.ide.iec61499.lang) - 6ed54515-acc8-4d1e-a16c-9fd6cfe951ea(MPS.Core) - 5aff85f5-c1e8-49b6-a1f1-66d79702cceb(org.fbme.ide.iec61499.adapter) - 1ed103c3-3aa6-49b7-9c21-6765ee11f224(MPS.Editor) - ce053d11-5ec7-4fac-b419-6715b4a97d3a(org.fbme.ide.richediting) - 3f233e7f-b8a6-46d2-a57f-795d56775243(Annotations) - ceab5195-25ea-4f22-9b92-103b95ca8c0c(jetbrains.mps.lang.core) - ce018f97-56b9-4ee7-9b5f-2d462b6628bf(org.fbme.platform.lib) - c6b02c2c-6484-48c4-ab6e-40937aeede2e(org.fbme.debugger.lib) - fa2f156c-8927-4ca1-847f-ba6f9ea8f1a5(org.fbme.richediting.lib) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/RuntimeTraceSynchronizer.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/RuntimeTraceSynchronizer.kt index a69c80fcc..4d80f2ac4 100644 --- a/code/debugger/src/main/kotlin/org/fbme/debugger/RuntimeTraceSynchronizer.kt +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/RuntimeTraceSynchronizer.kt @@ -7,6 +7,7 @@ import org.fbme.debugger.common.resolveFB import org.fbme.debugger.common.resolvePath import org.fbme.debugger.common.state.ResourceState import org.fbme.debugger.common.trace.ExecutionTrace +import org.fbme.debugger.common.trace.TraceItem import org.fbme.debugger.common.typeOfParameter import org.fbme.debugger.common.value.Value import org.fbme.debugger.common.valueOfParameter @@ -28,6 +29,12 @@ class RuntimeTraceSynchronizer( private val resourceDeclaration: ResourceDeclaration, private val trace: ExecutionTrace, ) { + inner class TraceNode( + val traceSegment: List, + val simulator: ResourceSimulator, + val parent: TraceNode? = null + ) + private val watcherFacade = project.service() private val readWatchesRequests = mutableListOf>() @@ -72,77 +79,98 @@ class RuntimeTraceSynchronizer( } private fun processReadWatchesRequests() { - var currentStateIndex = 0 - var curSimulator: ResourceSimulator? = null + initPorts() - for (functionBlock in resourceDeclaration.allFunctionBlocks()) { - for (parameter in functionBlock.parameters) { - val portName = parameter.parameterReference.getTarget()!!.name - val value = Value.fromSTLiteral(parameter.value!!) - val fbState = (trace[currentStateIndex].state as ResourceState).children[functionBlock.name]!! - fbState.inputVariables[portName] = value - } - } + val initialState = trace.items.last().state as ResourceState - requestsLoop@ for (watches in readWatchesRequests) { - val newWatches = watches.mapKeys { it.key.resolve(repository) } + val startNode = TraceNode( + traceSegment = listOf(trace.items.last()), + simulator = ResourceSimulator(resourceDeclaration, initialState) + ) - for (i in currentStateIndex + 1 until trace.size) { - if (getChanges(trace[i].state as ResourceState, newWatches).isEmpty()) { - currentStateIndex = i - continue@requestsLoop - } - } - currentStateIndex = trace.size - 1 + var prevState = initialState + + var candidates = listOf(startNode) + + requestsLoop@ for ((watchesIndex, watches) in readWatchesRequests.withIndex()) { + val newWatches = watches.mapKeys { it.key.resolve(repository) } - val changes = getChanges(trace[currentStateIndex].state as ResourceState, newWatches) + val changes = getChanges(prevState, newWatches) if (changes.isEmpty()) { continue@requestsLoop } - val currentState = trace[currentStateIndex].state as ResourceState - - for ((path, _) in changes) { + val serviceOutputEventChanges = changes.filter { (path, _) -> + val fbType = resourceDeclaration.resolvePath(path.dropLast(1)) val portName = path.last() - val fbPath = path.dropLast(1) - val fbState = currentState.resolveFB(fbPath) - val typeOfParameter = fbState.typeOfParameter(portName) ?: error("parameter not found") - - val newState = currentState.copy() - - val resourceSimulator = ResourceSimulator( - resourceDeclaration, - newState, - ) - val traceSegment = resourceSimulator.trace - if (curSimulator != null) { - resourceSimulator.applyContext(curSimulator) - } - val fbSimulator = resourceSimulator.resolveSimulator(fbPath) as FBSimulator - - when (typeOfParameter) { - "Input Event", "Output Event" -> { - fbSimulator.triggerEvent(portName) - for ((index, traceItem) in traceSegment.drop(1).withIndex()) { - val changesOnSegment = getChanges(traceItem.state as ResourceState, newWatches) - if (changesOnSegment.isEmpty()) { - traceItem.synced = true - currentStateIndex += index + 1 - trace.addAll(traceSegment.drop(1)) - curSimulator = resourceSimulator - continue@requestsLoop - } + fbType is ServiceInterfaceFBTypeDeclaration && portName in fbType.outputEvents.map { it.name } + || fbType.name == "E_CYCLE" && portName == "EO" // TODO: handle these cases more generic way + } + + if (serviceOutputEventChanges.isNotEmpty()) { + val serviceEventPermutations = serviceOutputEventChanges.toList().permutations() + val newCandidates = mutableListOf() + candidatesLoop@ for (candidate in candidates) { + val traceSegment = candidate.traceSegment + for (permutation in serviceEventPermutations) { + val simulator = ResourceSimulator(resourceDeclaration, candidate.simulator.state.copy()).also { + it.applyContext(candidate.simulator) + } + for ((serviceEventPath, _) in permutation) { + val fbSimulator = simulator.resolveSimulator(serviceEventPath.dropLast(1)) as FBSimulator + fbSimulator.triggerEvent(serviceEventPath.last()) } + newCandidates += TraceNode( + traceSegment = traceSegment + simulator.trace.items.drop(1), + simulator = simulator, + parent = candidate.parent + ) } + } + candidates = newCandidates + } - // Skip everything except event triggers - "Input Variable", "Output Variable", "Internal Variable", "ECC State" -> { - // skip + val newCandidates = mutableListOf() + candidatesLoop@ for (candidate in candidates) { + val traceSegment = candidate.traceSegment + tailSearchLoop@ for ((itemIndex, traceItem) in traceSegment.withIndex()) { + val itemChanges = getChanges(traceItem.state as ResourceState, newWatches) + if (itemChanges.isEmpty()) { + prevState = traceItem.state + newCandidates += TraceNode( + traceSegment = traceSegment.takeLast(traceSegment.size - itemIndex), + simulator = candidate.simulator, + parent = TraceNode( + traceSegment = traceSegment.take(itemIndex), + simulator = candidate.simulator, + parent = candidate.parent + ) + ) + break@tailSearchLoop } - - else -> error("unknown type") } } + candidates = newCandidates + } + + var cur: TraceNode? = candidates.first() + val result = mutableListOf() + while (cur != null) { + result += cur.traceSegment.reversed() + cur = cur.parent + } + trace.addAll(result.reversed().drop(1)) + } + + private fun initPorts() { + for (functionBlock in resourceDeclaration.allFunctionBlocks()) { + for (parameter in functionBlock.parameters) { + val portName = parameter.parameterReference.getTarget()!!.name + val valueAsLiteral = parameter.value ?: continue + val value = Value.fromSTLiteral(valueAsLiteral) + val fbState = (trace.first().state as ResourceState).children[functionBlock.name]!! + fbState.inputVariables[portName] = value + } } } diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/Utils.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/Utils.kt new file mode 100644 index 000000000..613009442 --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/Utils.kt @@ -0,0 +1,26 @@ +package org.fbme.debugger + +import java.util.Collections.swap + +fun List.permutations(): List> { + val retVal: MutableList> = mutableListOf() + + fun generate(k: Int, list: List) { + // If only 1 element, just output the array + if (k == 1) { + retVal.add(list.toList()) + } else { + for (i in 0 until k) { + generate(k - 1, list) + if (k % 2 == 0) { + swap(list, i, k - 1) + } else { + swap(list, 0, k - 1) + } + } + } + } + + generate(this.count(), this.toList()) + return retVal +} \ No newline at end of file diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/common/value/Value.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/common/value/Value.kt index 13fc1d28b..a1dc17be3 100644 --- a/code/debugger/src/main/kotlin/org/fbme/debugger/common/value/Value.kt +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/common/value/Value.kt @@ -21,6 +21,7 @@ sealed interface Value { LiteralKind.STRING -> StringValue(literal.value as String) LiteralKind.WSTRING -> TODO("Not yet implemented") LiteralKind.TIME -> TimeValue(literal.value as String) + LiteralKind.REAL -> TODO("Not yet implemented") } } } diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/DebuggerToolWindow.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/DebuggerToolWindow.kt new file mode 100644 index 000000000..af0333960 --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/DebuggerToolWindow.kt @@ -0,0 +1,23 @@ +package org.fbme.debugger.plugin + +import com.intellij.openapi.components.service +import com.intellij.openapi.project.Project +import org.fbme.debugger.common.ui.DebuggerPanel +import org.fbme.lib.common.Declaration + +class DebuggerToolWindow(val project: Project) { + + private val panels = hashMapOf() + + fun getPanel(declaration: Declaration): DebuggerPanel? = panels[declaration] + + fun addPanel(declaration: Declaration, panel: DebuggerPanel) { + panels[declaration] = panel + } + + companion object { + val ID = "Debugger" + + fun getInstance(project: Project) = project.service() + } +} \ No newline at end of file diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/DebuggerToolWindowFactory.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/DebuggerToolWindowFactory.kt new file mode 100644 index 000000000..3a0fd129f --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/DebuggerToolWindowFactory.kt @@ -0,0 +1,11 @@ +package org.fbme.debugger.plugin + +import com.intellij.openapi.project.Project +import com.intellij.openapi.wm.ToolWindow +import com.intellij.openapi.wm.ToolWindowFactory + +class DebuggerToolWindowFactory: ToolWindowFactory { + + override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) { + } +} \ No newline at end of file diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/DeploymentAction_DeployResource.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/DeploymentAction_DeployResource.kt new file mode 100644 index 000000000..b099ad435 --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/DeploymentAction_DeployResource.kt @@ -0,0 +1,111 @@ +package org.fbme.debugger.plugin + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.components.service +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.progress.Task +import com.intellij.openapi.project.DumbAware +import com.intellij.openapi.wm.ToolWindowManager +import com.intellij.ui.content.ContentFactory +import org.fbme.debugger.RuntimeTraceSynchronizer +import org.fbme.debugger.common.state.ResourceState +import org.fbme.debugger.common.trace.ExecutionTrace +import org.fbme.debugger.common.ui.DebuggerPanel +import org.fbme.debugger.explanation.ExplanationProducer +import org.fbme.ide.iec61499.snashot.DeclarationSnapshot.Companion.create +import org.fbme.ide.platform.debugger.DevicesFacade +import org.fbme.ide.platform.debugger.WatchedValueListener +import org.fbme.ide.platform.debugger.WatcherFacade +import org.fbme.ide.richediting.actions.* +import org.fbme.ide.richediting.editor.RichEditorStyleAttributes +import org.fbme.ide.richediting.inspections.Inspection +import org.fbme.ide.richediting.inspections.InspectionManagerImpl +import org.fbme.lib.common.Declaration +import org.fbme.lib.iec61499.declarations.DeviceDeclaration +import org.fbme.lib.iec61499.declarations.ResourceDeclaration +import org.fbme.lib.iec61499.fbnetwork.PortPath +import org.fbme.lib.iec61499.instances.NetworkInstance +import java.io.IOException + + +class DeploymentAction_DeployResource : AnAction(), DumbAware { + + override fun update(event: AnActionEvent) { + event.executeReadAction { + event.presentation.isEnabledAndVisible = event.element() != null + } + } + + override fun actionPerformed(event: AnActionEvent) { + event.executeWriteActionInEditor { + val cell = event.cell!! + val resourceDeclaration = event.element()!! + val deviceDeclaration = resourceDeclaration.container as DeviceDeclaration + val project = event.project!! + val mpsProject = event.mpsProject!! + val devicesFacade = service() + val watcherFacade = project.service() + val manager = InspectionManagerImpl.getInstance(cell.editorComponent)!! + val networkInstance = NetworkInstance.createForResource(resourceDeclaration) + val inspector = manager.installInspector(networkInstance) { }!! + + object : Task.Backgroundable(project, "Deploying Resource") { + override fun run(indicator: ProgressIndicator) { + mpsProject.modelAccess.runReadAction { + try { + val connection = devicesFacade.attach(deviceDeclaration) + connection.createResourceNetwork(resourceDeclaration) + watcherFacade.watchResourceNetwork(resourceDeclaration) + watcherFacade.addWatchedValueListenersResourceNetwork(resourceDeclaration) { path -> + WatchedValueListener { newValue -> + inspector.setInspectionForPort(path, Inspection(newValue)) + } + } + connection.startResource(resourceDeclaration) + } catch (e: IOException) { + devicesFacade.invalidate(deviceDeclaration) + throw e + } + } + } + }.queue() + + val toolWindowManager = ToolWindowManager.getInstance(project) + val toolWindow = toolWindowManager.getToolWindow(DebuggerToolWindow.ID)!! + + val snapshot = create(resourceDeclaration) + val snapshotDeclaration = snapshot.snapshotDeclaration as ResourceDeclaration + val typeName = snapshotDeclaration.name + val resourceState = ResourceState(snapshotDeclaration) + val executionTrace = ExecutionTrace(resourceState) + val explanationProducer = ExplanationProducer(executionTrace, snapshotDeclaration) + + val traceSynchronizer = + RuntimeTraceSynchronizer(event.getData(CommonDataKeys.PROJECT)!!, resourceDeclaration, executionTrace) + RuntimeTraceSynchronizer.addTraceSynchronizer(resourceDeclaration, traceSynchronizer) + traceSynchronizer.startMonitoring() + + val debuggerPanel = DebuggerPanel( + project, + mpsProject, + executionTrace, + snapshotDeclaration, + resourceDeclaration, + explanationProducer, + inspector + ) + DebuggerToolWindow.getInstance(project).addPanel(resourceDeclaration, debuggerPanel) + + val content = + ContentFactory.SERVICE.getInstance().createContent(debuggerPanel.toolWindowPanel, typeName, false) + content.isPinnable = true + content.isPinned = true + + toolWindow.contentManager.addContent(content) + toolWindow.contentManager.setSelectedContent(content, true) + toolWindow.show() + } + } +} diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/DeploymentAction_StopResource.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/DeploymentAction_StopResource.kt new file mode 100644 index 000000000..e2c571c94 --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/DeploymentAction_StopResource.kt @@ -0,0 +1,58 @@ +package org.fbme.debugger.plugin + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.components.service +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.progress.Task +import com.intellij.openapi.project.DumbAware +import org.fbme.debugger.RuntimeTraceSynchronizer +import org.fbme.ide.platform.debugger.DevicesFacade +import org.fbme.ide.richediting.actions.element +import org.fbme.ide.richediting.actions.executeReadAction +import org.fbme.ide.richediting.actions.executeWriteActionInEditor +import org.fbme.lib.iec61499.declarations.DeviceDeclaration +import org.fbme.lib.iec61499.declarations.ResourceDeclaration +import java.io.IOException + + +class DeploymentAction_StopResource : AnAction(), DumbAware { + + override fun update(event: AnActionEvent) { + event.executeReadAction { + event.presentation.isEnabledAndVisible = event.element() != null + } + } + + override fun actionPerformed(event: AnActionEvent) { + event.executeWriteActionInEditor { + val facade = service() + val project = event.project!! + val resourceDeclaration = event.element()!! + val deviceDeclaration = resourceDeclaration.container as DeviceDeclaration + + object : Task.Backgroundable(project, "Stopping Resource") { + override fun run(indicator: ProgressIndicator) { + event.executeReadAction { + try { + val connection = facade.attach(deviceDeclaration) + connection.killResource(resourceDeclaration) + connection.deleteResource(resourceDeclaration) + } catch (e: IOException) { + facade.invalidate(deviceDeclaration) + throw e + } + } + } + }.queue() + + val traceSynchronizer = RuntimeTraceSynchronizer.getInstance(resourceDeclaration)!! + traceSynchronizer.endMonitoring() + RuntimeTraceSynchronizer.removeTraceSynchronizer(resourceDeclaration) + + val debuggerTool = project.service() + val panel = debuggerTool.getPanel(resourceDeclaration) + panel!!.initUI() + } + } +} diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/InspectionsManager_ProjectPluginPart.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/InspectionsManager_ProjectPluginPart.kt new file mode 100644 index 000000000..303fd0616 --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/InspectionsManager_ProjectPluginPart.kt @@ -0,0 +1,19 @@ +package org.fbme.debugger.plugin + +import jetbrains.mps.openapi.editor.extensions.EditorExtensionRegistry +import jetbrains.mps.plugins.part.ProjectPluginPart +import jetbrains.mps.project.MPSProject +import org.fbme.ide.richediting.inspections.InspectionManagerImpl + +/*Generated by MPS */ +class InspectionsManager_ProjectPluginPart : ProjectPluginPart() { + override fun init(project: MPSProject) { + val reg = project.getComponent(EditorExtensionRegistry::class.java) + reg.registerExtension(InspectionManagerImpl.EDITOR_EXTENSION) + } + + override fun dispose(project: MPSProject) { + val reg = project.getComponent(EditorExtensionRegistry::class.java) + reg.unregisterExtension(InspectionManagerImpl.EDITOR_EXTENSION) + } +} diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_DisableWatching.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_DisableWatching.kt new file mode 100644 index 000000000..afdf30aab --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_DisableWatching.kt @@ -0,0 +1,33 @@ +package org.fbme.debugger.plugin + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.DumbAware +import org.fbme.ide.richediting.actions.cell +import org.fbme.ide.richediting.actions.element +import org.fbme.ide.richediting.actions.executeReadAction +import org.fbme.ide.richediting.actions.executeWriteActionInEditor +import org.fbme.ide.richediting.editor.RichEditorStyleAttributes +import org.fbme.ide.richediting.inspections.InspectionManagerImpl.Companion.getInstance +import org.fbme.lib.iec61499.declarations.ResourceDeclaration + +class MonitoringAction_DisableWatching : AnAction(), DumbAware { + + override fun update(event: AnActionEvent) { + event.presentation.isEnabledAndVisible = false + val editorCell = event.cell ?: return + val project = event.project ?: return + event.executeReadAction { + val element = event.element() ?: return@executeReadAction + event.presentation.isEnabledAndVisible = NetworkWatchActions(element, editorCell, project).inspector != null + } + } + + override fun actionPerformed(event: AnActionEvent) { + event.executeWriteActionInEditor { + val cell = event.cell!! + val network = cell.style.get(RichEditorStyleAttributes.NETWORK_INSTANCE) + getInstance(cell.editorComponent)?.disposeInspector(network) + } + } +} diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_EnableWatching.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_EnableWatching.kt new file mode 100644 index 000000000..f604ee11d --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_EnableWatching.kt @@ -0,0 +1,39 @@ +package org.fbme.debugger.plugin + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.DumbAware +import org.fbme.ide.richediting.actions.cell +import org.fbme.ide.richediting.actions.element +import org.fbme.ide.richediting.actions.executeReadAction +import org.fbme.ide.richediting.actions.executeWriteActionInEditor +import org.fbme.ide.richediting.editor.RichEditorStyleAttributes +import org.fbme.ide.richediting.inspections.InspectionManagerImpl +import org.fbme.lib.iec61499.declarations.ResourceDeclaration + + +class MonitoringAction_EnableWatching : AnAction(), DumbAware { + + override fun update(event: AnActionEvent) { + event.presentation.isEnabledAndVisible = false + val editorCell = event.cell ?: return + val project = event.project ?: return + event.executeReadAction { + val element = event.element() ?: return@executeReadAction + val actions = NetworkWatchActions(element, editorCell, project) + event.presentation.isEnabledAndVisible = actions.inspector != null && actions.networkInstance != null + } + } + + override fun actionPerformed(event: AnActionEvent) { + event.executeWriteActionInEditor { + val cell = event.cell!! + val project = event.project!! + val element = event.element()!! + val network = cell.style.get(RichEditorStyleAttributes.NETWORK_INSTANCE) + val actions = NetworkWatchActions(element, cell, project) + InspectionManagerImpl.getInstance(cell.editorComponent) + ?.installInspector(network) { actions.inspector = null } + } + } +} diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_UnwatchECCState.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_UnwatchECCState.kt new file mode 100644 index 000000000..ee9c55856 --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_UnwatchECCState.kt @@ -0,0 +1,32 @@ +package org.fbme.debugger.plugin + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.DumbAware +import org.fbme.ide.richediting.actions.cell +import org.fbme.ide.richediting.actions.element +import org.fbme.ide.richediting.actions.executeReadAction +import org.fbme.ide.richediting.actions.executeWriteActionInEditor +import org.fbme.lib.iec61499.declarations.ResourceDeclaration + +class MonitoringAction_UnwatchECCState : AnAction(), DumbAware { + + override fun update(event: AnActionEvent) { + event.presentation.isEnabledAndVisible = false + val editorCell = event.cell ?: return + val project = event.project ?: return + event.executeReadAction { + val element = event.element() ?: return@executeReadAction + event.presentation.isEnabledAndVisible = NetworkWatchActions(element, editorCell, project).hasWatchedEcc() + } + } + + override fun actionPerformed(event: AnActionEvent) { + event.executeWriteActionInEditor { + val cell = event.cell!! + val element = event.element()!! + val project = event.project!! + NetworkWatchActions(element, cell, project).unwatchEcc() + } + } +} diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_UnwatchFB.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_UnwatchFB.kt new file mode 100644 index 000000000..3b84987f1 --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_UnwatchFB.kt @@ -0,0 +1,36 @@ +package org.fbme.debugger.plugin + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.DumbAware +import org.fbme.ide.richediting.actions.cell +import org.fbme.ide.richediting.actions.element +import org.fbme.ide.richediting.actions.executeReadAction +import org.fbme.ide.richediting.actions.executeWriteActionInEditor +import org.fbme.lib.iec61499.declarations.ResourceDeclaration + +class MonitoringAction_UnwatchFB : AnAction(), DumbAware { + + override fun update(event: AnActionEvent) { + event.presentation.isEnabledAndVisible = false + val editorCell = event.cell ?: return + val project = event.project ?: return + event.executeReadAction { + val element = event.element() ?: return@executeReadAction + val actions = NetworkWatchActions(element, editorCell, project) + if (actions.hasWatchedFunctionBlock()) { + event.presentation.isEnabledAndVisible = true + event.presentation.text = "Stop Watching for Function block " + actions.functionBlock!!.name + } + } + } + + override fun actionPerformed(event: AnActionEvent) { + event.executeWriteActionInEditor { + val cell = event.cell!! + val element = event.element()!! + val project = event.project!! + NetworkWatchActions(element, cell, project).unwatchFunctionBlock() + } + } +} diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_UnwatchPort.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_UnwatchPort.kt new file mode 100644 index 000000000..5286de3eb --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_UnwatchPort.kt @@ -0,0 +1,36 @@ +package org.fbme.debugger.plugin + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.DumbAware +import org.fbme.ide.richediting.actions.cell +import org.fbme.ide.richediting.actions.element +import org.fbme.ide.richediting.actions.executeReadAction +import org.fbme.ide.richediting.actions.executeWriteActionInEditor +import org.fbme.lib.iec61499.declarations.ResourceDeclaration + +class MonitoringAction_UnwatchPort : AnAction(), DumbAware { + + override fun update(event: AnActionEvent) { + event.presentation.isEnabledAndVisible = false + val editorCell = event.cell ?: return + val project = event.project ?: return + event.executeReadAction { + val element = event.element() ?: return@executeReadAction + val actions = NetworkWatchActions(element, editorCell, project) + if (actions.hasWatchedPort()) { + event.presentation.isEnabledAndVisible = true + event.presentation.text = "Stop Watching for Port " + actions.portToWatch() + } + } + } + + override fun actionPerformed(event: AnActionEvent) { + event.executeWriteActionInEditor { + val cell = event.cell!! + val element = event.element()!! + val project = event.project!! + NetworkWatchActions(element, cell, project).unwatchPort() + } + } +} diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_UnwatchSelectedFBs.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_UnwatchSelectedFBs.kt new file mode 100644 index 000000000..463a6fae5 --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_UnwatchSelectedFBs.kt @@ -0,0 +1,32 @@ +package org.fbme.debugger.plugin + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import org.fbme.ide.richediting.actions.cell +import org.fbme.ide.richediting.actions.element +import org.fbme.ide.richediting.actions.executeReadAction +import org.fbme.ide.richediting.actions.executeWriteActionInEditor +import org.fbme.lib.iec61499.declarations.ResourceDeclaration + +class MonitoringAction_UnwatchSelectedFBs : AnAction() { + + override fun update(event: AnActionEvent) { + event.presentation.isEnabledAndVisible = false + val editorCell = event.cell ?: return + val project = event.project ?: return + event.executeReadAction { + val element = event.element() ?: return@executeReadAction + event.presentation.isEnabledAndVisible = + NetworkWatchActions(element, editorCell, project).hasWatchedFunctionBlocksInSelection() + } + } + + override fun actionPerformed(event: AnActionEvent) { + event.executeWriteActionInEditor { + val cell = event.cell!! + val element = event.element()!! + val project = event.project!! + NetworkWatchActions(element, cell, project).unwatchSelectedFunctionBlocks() + } + } +} diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_WatchECCState.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_WatchECCState.kt new file mode 100644 index 000000000..b2a2d9b59 --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_WatchECCState.kt @@ -0,0 +1,32 @@ +package org.fbme.debugger.plugin + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import org.fbme.ide.richediting.actions.cell +import org.fbme.ide.richediting.actions.element +import org.fbme.ide.richediting.actions.executeReadAction +import org.fbme.ide.richediting.actions.executeWriteActionInEditor +import org.fbme.lib.iec61499.declarations.ResourceDeclaration + + +class MonitoringAction_WatchECCState : AnAction() { + + override fun update(event: AnActionEvent) { + event.presentation.isEnabledAndVisible = false + val editorCell = event.cell ?: return + val project = event.project ?: return + event.executeReadAction { + val element = event.element() ?: return@executeReadAction + event.presentation.isEnabledAndVisible = NetworkWatchActions(element, editorCell, project).hasEccToWatch() + } + } + + override fun actionPerformed(event: AnActionEvent) { + event.executeWriteActionInEditor { + val cell = event.cell!! + val element = event.element()!! + val project = event.project!! + NetworkWatchActions(element, cell, project).watchEcc() + } + } +} diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_WatchFB.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_WatchFB.kt new file mode 100644 index 000000000..09b5e12db --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_WatchFB.kt @@ -0,0 +1,35 @@ +package org.fbme.debugger.plugin + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import org.fbme.ide.richediting.actions.cell +import org.fbme.ide.richediting.actions.element +import org.fbme.ide.richediting.actions.executeReadAction +import org.fbme.ide.richediting.actions.executeWriteActionInEditor +import org.fbme.lib.iec61499.declarations.ResourceDeclaration + +class MonitoringAction_WatchFB : AnAction() { + + override fun update(event: AnActionEvent) { + event.presentation.isEnabledAndVisible = false + val editorCell = event.cell ?: return + val project = event.project ?: return + event.executeReadAction { + val element = event.element() ?: return@executeReadAction + val actions = NetworkWatchActions(element, editorCell, project) + if (actions.hasFunctionBlockToWatch()) { + event.presentation.isEnabledAndVisible = true + event.presentation.text = "Watch for Function block " + actions.functionBlock!!.name + } + } + } + + override fun actionPerformed(event: AnActionEvent) { + event.executeWriteActionInEditor { + val cell = event.cell!! + val element = event.element()!! + val project = event.project!! + NetworkWatchActions(element, cell, project).watchFunctionBlock() + } + } +} diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_WatchPort.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_WatchPort.kt new file mode 100644 index 000000000..0a38c57e3 --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_WatchPort.kt @@ -0,0 +1,36 @@ +package org.fbme.debugger.plugin + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import org.fbme.ide.richediting.actions.cell +import org.fbme.ide.richediting.actions.element +import org.fbme.ide.richediting.actions.executeReadAction +import org.fbme.ide.richediting.actions.executeWriteActionInEditor +import org.fbme.lib.iec61499.declarations.ResourceDeclaration + + +class MonitoringAction_WatchPort : AnAction() { + + override fun update(event: AnActionEvent) { + event.presentation.isEnabledAndVisible = false + val editorCell = event.cell ?: return + val project = event.project ?: return + event.executeReadAction { + val element = event.element() ?: return@executeReadAction + val actions = NetworkWatchActions(element, editorCell, project) + if (actions.hasPortToWatch()) { + event.presentation.isEnabledAndVisible = true + event.presentation.text = "Watch for Port " + actions.portToWatch() + } + } + } + + override fun actionPerformed(event: AnActionEvent) { + event.executeWriteActionInEditor { + val cell = event.cell!! + val element = event.element()!! + val project = event.project!! + NetworkWatchActions(element, cell, project).watchPort() + } + } +} diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_WatchSelectedFBs.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_WatchSelectedFBs.kt new file mode 100644 index 000000000..a02f8c54f --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/MonitoringAction_WatchSelectedFBs.kt @@ -0,0 +1,33 @@ +package org.fbme.debugger.plugin + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.DumbAware +import org.fbme.ide.richediting.actions.cell +import org.fbme.ide.richediting.actions.element +import org.fbme.ide.richediting.actions.executeReadAction +import org.fbme.ide.richediting.actions.executeWriteActionInEditor +import org.fbme.lib.iec61499.declarations.ResourceDeclaration + +class MonitoringAction_WatchSelectedFBs : AnAction(), DumbAware { + + override fun update(event: AnActionEvent) { + event.presentation.isEnabledAndVisible = false + val editorCell = event.cell ?: return + val project = event.project ?: return + event.executeReadAction { + val element = event.element() ?: return@executeReadAction + event.presentation.isEnabledAndVisible = + NetworkWatchActions(element, editorCell, project).hasFunctionBlocskToWatchInSelection() + } + } + + override fun actionPerformed(event: AnActionEvent) { + event.executeWriteActionInEditor { + val cell = event.cell!! + val element = event.element()!! + val project = event.project!! + NetworkWatchActions(element, cell, project).watchSelectedFunctionBlocks() + } + } +} diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/NetworkWatchActions.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/NetworkWatchActions.kt new file mode 100644 index 000000000..6dcafd4ac --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/NetworkWatchActions.kt @@ -0,0 +1,292 @@ +package org.fbme.debugger.plugin + +import com.intellij.openapi.components.service +import com.intellij.openapi.project.Project +import jetbrains.mps.nodeEditor.MPSColors +import jetbrains.mps.openapi.editor.cells.EditorCell +import org.fbme.ide.iec61499.repository.PlatformElement +import org.fbme.ide.platform.debugger.Watchable +import org.fbme.ide.platform.debugger.WatchablePath +import org.fbme.ide.platform.debugger.WatchedValueListener +import org.fbme.ide.platform.debugger.WatcherFacade +import org.fbme.ide.richediting.editor.RichEditorStyleAttributes +import org.fbme.ide.richediting.inspections.Inspection +import org.fbme.ide.richediting.inspections.NetworkInspector +import org.fbme.ide.richediting.plugin.NetworkObservationKeys +import org.fbme.ide.richediting.viewmodel.FunctionBlockView +import org.fbme.lib.common.Declaration +import org.fbme.lib.iec61499.declarations.BasicFBTypeDeclaration +import org.fbme.lib.iec61499.declarations.ResourceDeclaration +import org.fbme.lib.iec61499.descriptors.FBPortDescriptor +import org.fbme.lib.iec61499.descriptors.FBTypeDescriptor +import org.fbme.lib.iec61499.fbnetwork.FunctionBlockDeclaration +import org.fbme.lib.iec61499.fbnetwork.FunctionBlockDeclarationBase +import org.fbme.lib.iec61499.fbnetwork.PortPath +import org.fbme.lib.iec61499.instances.FunctionBlockInstance +import org.fbme.lib.iec61499.instances.NetworkInstance + +class NetworkWatchActions( + private val resourceDeclaration: ResourceDeclaration, + editorCell: EditorCell, + project: Project, +) { + private val watchers = project.service() + + private val style = editorCell.style + + private val resourceNode get() = (resourceDeclaration as PlatformElement).node + + val networkInstance: NetworkInstance? + get() = style.get(RichEditorStyleAttributes.NETWORK_INSTANCE) + + val functionBlock: FunctionBlockDeclarationBase? = style.get(RichEditorStyleAttributes.FB) + + val port: PortPath? + get() { + val portDescriptor: FBPortDescriptor = style.get(RichEditorStyleAttributes.PORT) ?: return null + return PortPath.createPortPath(functionBlock, portDescriptor.connectionKind, portDescriptor.declaration!!) + } + + val selectedFunctionBlocks: Sequence + get() { + val selectedComponents = + style.get(RichEditorStyleAttributes.SELECTED_FBS)?.selectedComponents ?: emptyList() + return selectedComponents + .asSequence() + .filterIsInstance() + .map { it.component } + } + + + fun highlightEventConnections() { + // to test highlight connections only, TODO write normal tests + val inspector: NetworkInspector = inspector ?: return + val networkInstance: NetworkInstance = networkInstance ?: return + for (connection in networkInstance.networkDeclaration.eventConnections) { + inspector.setHighlightingForConnection(connection, MPSColors.YELLOW) + } + } + + fun hasPortToWatch(): Boolean { + if (inspector == null) { + return false + } + val port: PortPath<*>? = port + return port != null && getPortListener(port) == null + } + + fun hasWatchedPort(): Boolean { + if (inspector == null) { + return false + } + val port: PortPath<*>? = port + return port != null && getPortListener(port) != null + } + + fun portToWatch(): String = + style.get(RichEditorStyleAttributes.PORT).name + + fun hasWatchedFunctionBlock(): Boolean = + inspector != null && functionBlock != null && functionBlock.ports.any { getPortListener(it) != null } + + fun hasWatchedFunctionBlocksInSelection(): Boolean = + inspector != null && selectedFunctionBlocks.any { it.ports.any { getPortListener(it) != null } } + + fun hasFunctionBlockToWatch(): Boolean = + inspector != null && functionBlock != null && functionBlock.ports.any { getPortListener(it) == null } + + fun hasFunctionBlocskToWatchInSelection(): Boolean = + inspector != null && selectedFunctionBlocks.any { it.ports.any { getPortListener(it) == null } } + + fun unwatchSelectedFunctionBlocks() { + for (functionBlock in selectedFunctionBlocks) { + unwatchFunctionBlock(functionBlock) + } + } + + fun unwatchFunctionBlock() { + unwatchFunctionBlock(functionBlock ?: return) + } + + private fun unwatchFunctionBlock(functionBlock: FunctionBlockDeclarationBase) { + val provider: NetworkInspector = inspector ?: return + for (port in functionBlock.ports) { + val listener: WatchedValueListener = getPortListener(port) ?: continue + val watchable = Watchable(createWatchablePath(functionBlock), port.portTarget.name) + watchers.unwatch(watchable) + watchers.removeWatchedValueListener(watchable.serialize(), listener) + provider.setInspectionForPort(port, null) + setPortListener(port, null) + } + } + + fun watchSelectedFunctionBlocks() { + for (functionBlock in selectedFunctionBlocks) { + watchFunctionBlock(functionBlock) + } + } + + fun watchFunctionBlock() { + watchFunctionBlock(functionBlock as FunctionBlockDeclaration) + } + + private fun watchFunctionBlock(functionBlock: FunctionBlockDeclarationBase) { + val provider: NetworkInspector = inspector ?: return + for (port in functionBlock.ports) { + if (getPortListener(port) != null) { + continue + } + val portName: String = port.portTarget.name + val watchable = Watchable(createWatchablePath(functionBlock), portName) + val type: FBTypeDescriptor = functionBlock.type + if (watchable.portDescriptor == null) { + val eventInputPorts: List = type.eventInputPorts + findPortDescriptor(eventInputPorts, portName, watchable) + } + if (watchable.portDescriptor == null) { + val eventOutputPorts: List = type.eventOutputPorts + findPortDescriptor(eventOutputPorts, portName, watchable) + } + if (watchable.portDescriptor == null) { + val dataInputPorts: List = type.dataInputPorts + findPortDescriptor(dataInputPorts, portName, watchable) + } + if (watchable.portDescriptor == null) { + val dataOutputPorts: List = type.dataOutputPorts + findPortDescriptor(dataOutputPorts, portName, watchable) + } + val listener = object : WatchedValueListener { + override fun onValueChanged(newValue: String) { + provider.setInspectionForPort(port, Inspection(newValue)) + } + } + watchers.watch(watchable) + watchers.addWatchedValueListener(watchable.serialize(), listener) + setPortListener(port, listener) + } + } + + private fun findPortDescriptor(ports: List, portName: String, watchable: Watchable) { + for (portDescriptor in ports) { + if (portDescriptor.name === portName) { + watchable.portDescriptor = portDescriptor + break + } + } + } + + fun createWatchablePath(): WatchablePath { + return createWatchablePath(functionBlock as FunctionBlockDeclaration) + } + + private fun createWatchablePath(fb: FunctionBlockDeclarationBase): WatchablePath { + var networkInstance: NetworkInstance = style.get(RichEditorStyleAttributes.NETWORK_INSTANCE) + val nestedList: MutableList = arrayListOf() + var parentInstance: FunctionBlockInstance? = networkInstance.parent as? FunctionBlockInstance + while (parentInstance != null) { + val functionBlock: FunctionBlockDeclaration = parentInstance.declaration as FunctionBlockDeclaration + nestedList += functionBlock + networkInstance = parentInstance.parent + parentInstance = networkInstance.parent as? FunctionBlockInstance + } + val pathList: MutableList = arrayListOf() + pathList += nestedList.reversed() + pathList += fb + return WatchablePath(resourceDeclaration, pathList) + } + + fun createWatchableForPort(): Watchable { + val portDescriptor: FBPortDescriptor = style.get(RichEditorStyleAttributes.PORT) + val watchable = Watchable(createWatchablePath(), portDescriptor.name) + watchable.portDescriptor = portDescriptor + return watchable + } + + fun createWatchableForEcc(): Watchable { + return Watchable(createWatchablePath(), "\$ECC") + } + + fun watchPort() { + val provider: NetworkInspector = inspector ?: return + val port: PortPath<*> = port ?: return + val watchable = createWatchableForPort() + val listener = WatchedValueListener { newValue -> provider.setInspectionForPort(port, Inspection(newValue)) } + watchers.watch(watchable) + watchers.addWatchedValueListener(watchable.serialize(), listener) + setPortListener(port, listener) + } + + fun unwatchPort() { + val provider: NetworkInspector = inspector ?: return + val watchable = createWatchableForPort() + val port: PortPath<*> = port ?: return + watchers.unwatch(watchable) + getPortListener(port)?.let { watchers.removeWatchedValueListener(watchable.serialize(), it) } + provider.setInspectionForPort(port, null) + setPortListener(port, null) + } + + fun hasEccToWatch(): Boolean { + if (inspector == null) { + return false + } + val fb: FunctionBlockDeclarationBase = style.get(RichEditorStyleAttributes.FB) ?: return false + return fb.type.declaration is BasicFBTypeDeclaration && getECCStateListener(fb) == null + } + + fun hasWatchedEcc(): Boolean { + if (inspector == null) { + return false + } + val fb: FunctionBlockDeclarationBase = style.get(RichEditorStyleAttributes.FB) ?: return false + return fb.type.declaration is BasicFBTypeDeclaration && getECCStateListener(fb) != null + } + + fun watchEcc() { + val inspector: NetworkInspector = inspector ?: return + val watchable = createWatchableForEcc() + val fb: FunctionBlockDeclarationBase = style.get(RichEditorStyleAttributes.FB) + val listener = WatchedValueListener { newValue -> + inspector.setInspectionForComponent(fb, Inspection("State: $newValue")) + } + watchers.watch(watchable) + watchers.addWatchedValueListener(watchable.serialize(), listener) + setECCStateListener(fb, listener) + } + + fun unwatchEcc() { + val provider: NetworkInspector = inspector ?: return + val watchable = createWatchableForEcc() + val fb: FunctionBlockDeclarationBase = style.get(RichEditorStyleAttributes.FB) + watchers.unwatch(watchable) + getECCStateListener(fb)?.let { watchers.removeWatchedValueListener(watchable.serialize(), it) } + provider.setInspectionForComponent(fb, null) + setECCStateListener(fb, null) + } + + var inspector: NetworkInspector? + get() = networkInstance?.let { NetworkObservationKeys.inspector(it) as? NetworkInspector } + set(inspector) { + networkInstance?.let { resourceNode.putUserObject(NetworkObservationKeys.inspector(it), inspector) } + } + + fun getPortListener(port: PortPath<*>): WatchedValueListener? { + val key = NetworkObservationKeys.portListener(networkInstance ?: return null, port) + return resourceNode.getUserObject(key) as? WatchedValueListener + } + + fun setPortListener(port: PortPath<*>, listener: WatchedValueListener?) { + val key = NetworkObservationKeys.portListener(networkInstance ?: return, port) + resourceNode.putUserObject(key, listener) + } + + fun getECCStateListener(fb: FunctionBlockDeclarationBase): WatchedValueListener? { + val key = NetworkObservationKeys.eccStateListener(networkInstance ?: return null, fb) + return resourceNode.getUserObject(key) as? WatchedValueListener + } + + fun setECCStateListener(fb: FunctionBlockDeclarationBase, listener: WatchedValueListener?) { + val key = NetworkObservationKeys.eccStateListener(networkInstance ?: return, fb) + resourceNode.putUserObject(key, listener) + } +} diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/SimulateExecutionAction.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/SimulateExecutionAction.kt new file mode 100644 index 000000000..ed2baa03b --- /dev/null +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/plugin/SimulateExecutionAction.kt @@ -0,0 +1,102 @@ +package org.fbme.debugger.plugin + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.DumbAware +import com.intellij.openapi.wm.ToolWindowManager +import com.intellij.ui.content.ContentFactory +import jetbrains.mps.project.MPSProject +import org.fbme.debugger.common.trace.ExecutionTrace +import org.fbme.debugger.explanation.ExplanationProducer +import org.fbme.debugger.simulator.BasicFBSimulator +import org.fbme.debugger.simulator.CompositeFBSimulator +import org.fbme.debugger.simulator.ResourceSimulator +import org.fbme.debugger.simulator.ui.SimulatorPanel +import org.fbme.ide.iec61499.snashot.DeclarationSnapshot +import org.fbme.ide.richediting.actions.cell +import org.fbme.ide.richediting.actions.executeWriteActionInEditor +import org.fbme.ide.richediting.editor.RichEditorStyleAttributes +import org.fbme.ide.richediting.inspections.InspectionManager +import org.fbme.ide.richediting.inspections.InspectionManagerImpl +import org.fbme.lib.common.Declaration +import org.fbme.lib.iec61499.declarations.* +import org.fbme.lib.iec61499.instances.NetworkInstance + +class SimulateExecutionAction : AnAction(), DumbAware { + + override fun update(event: AnActionEvent) { + event.presentation.isEnabledAndVisible = false + val cell = event.cell ?: return + if (cell.style.get(RichEditorStyleAttributes.FB) != null) { + event.presentation.isEnabledAndVisible = true + return + } + val fbNetwork = cell.style.get(RichEditorStyleAttributes.NETWORK) + if (fbNetwork?.container is FBTypeDeclaration || fbNetwork?.container is ResourceDeclaration) { + event.presentation.isEnabledAndVisible = true + return + } + val ecc = cell.style.get(RichEditorStyleAttributes.ECC) + if (ecc?.container != null) { + event.presentation.isEnabledAndVisible = true + } + } + + override fun actionPerformed(event: AnActionEvent) { + event.executeWriteActionInEditor { + val project = event.project!! + val mpsProject = project.getComponent(MPSProject::class.java) + val toolWindowManager = ToolWindowManager.getInstance(project) + val toolWindow = toolWindowManager.getToolWindow(DebuggerToolWindow.ID)!! + val cell = event.cell!! + val style = cell.style + + val typeDeclaration = style.get(RichEditorStyleAttributes.FB)?.type?.declaration + ?: style.get(RichEditorStyleAttributes.NETWORK)?.container + ?.let { it as? FBTypeDeclaration ?: it as? ResourceDeclaration } + ?: style.get(RichEditorStyleAttributes.ECC)?.container!! + + val snapshot: DeclarationSnapshot = DeclarationSnapshot.create(typeDeclaration) + val snapshotDeclaration: Declaration = snapshot.snapshotDeclaration + val typeName = snapshotDeclaration.name + + val simulator = when (snapshotDeclaration) { + is BasicFBTypeDeclaration -> BasicFBSimulator(snapshotDeclaration) + is CompositeFBTypeDeclaration -> CompositeFBSimulator(snapshotDeclaration) + is ResourceDeclaration -> ResourceSimulator(snapshotDeclaration) + else -> error("Unsupported declaration: $snapshotDeclaration") + } + + val trace: ExecutionTrace = simulator.trace + val explanationProducer = ExplanationProducer(trace, snapshotDeclaration) + + val inspector = (snapshotDeclaration as? DeclarationWithNetwork)?.let { + val manager: InspectionManager = InspectionManagerImpl.getInstance(cell.editorComponent)!! + val networkInstance: NetworkInstance = cell.style.get(RichEditorStyleAttributes.NETWORK_INSTANCE) + if (networkInstance.networkDeclaration.container == typeDeclaration) { + manager.installInspector(networkInstance) { }!! + } else null + } + + val simulatorPanel = SimulatorPanel( + project, + mpsProject, + trace, + snapshotDeclaration, + typeDeclaration, + simulator, + explanationProducer, + inspector + ) + val simulatorPanelComponent = simulatorPanel.toolWindowPanel + + val content = ContentFactory.SERVICE.getInstance().createContent(simulatorPanelComponent, typeName, false) + content.isPinnable = true + content.isPinned = true + + toolWindow.contentManager.addContent(content) + toolWindow.contentManager.setSelectedContent(content, true) + toolWindow.show() + } + } +} diff --git a/code/debugger/src/main/resources/META-INF/plugin.xml b/code/debugger/src/main/resources/META-INF/plugin.xml index ca7d686db..67a3cdbfd 100644 --- a/code/debugger/src/main/resources/META-INF/plugin.xml +++ b/code/debugger/src/main/resources/META-INF/plugin.xml @@ -12,6 +12,63 @@ fbme.richediting - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps index 45e2d2b89..ebc3e744a 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps @@ -1248,7 +1248,6 @@ - diff --git a/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.behavior.mps b/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.behavior.mps index 3e37b5010..a3c3c5616 100644 --- a/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.behavior.mps +++ b/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.behavior.mps @@ -563,7 +563,7 @@ - + @@ -890,5 +890,28 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.editor.mps b/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.editor.mps index f53702bf8..3e6ce0ea7 100644 --- a/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.editor.mps +++ b/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.editor.mps @@ -40,6 +40,9 @@ + + + @@ -313,6 +316,7 @@ + @@ -413,6 +417,9 @@ + + + @@ -707,7 +714,7 @@ - + @@ -9709,7 +9716,7 @@ - + @@ -9718,12 +9725,12 @@ - + - + @@ -9879,5 +9886,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.structure.mps b/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.structure.mps index b9921281a..36b4b0dc4 100644 --- a/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.structure.mps +++ b/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.structure.mps @@ -143,7 +143,7 @@ - + @@ -608,8 +608,8 @@ - - + + @@ -944,14 +944,14 @@ - + - + @@ -1176,5 +1176,22 @@ + + + + + + + + + + + + + + + + + diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.common.mps b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.common.mps index fd38ac1ec..557615311 100644 --- a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.common.mps +++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.common.mps @@ -843,7 +843,7 @@ - + @@ -1190,7 +1190,7 @@ - + @@ -1643,26 +1643,53 @@ - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.ecc.mps b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.ecc.mps index 9b0d3f13f..d3fca0692 100644 --- a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.ecc.mps +++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.ecc.mps @@ -246,18 +246,18 @@ - - - + + + - + - + - - + + @@ -1075,6 +1075,9 @@ + + + @@ -1083,9 +1086,6 @@ - - - @@ -1840,6 +1840,9 @@ + + + @@ -1848,9 +1851,6 @@ - - - @@ -2389,7 +2389,6 @@ - diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.fbnetwork.mps b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.fbnetwork.mps index a96f9933b..c407d7d91 100644 --- a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.fbnetwork.mps +++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.fbnetwork.mps @@ -172,6 +172,11 @@ + + + + + @@ -210,7 +215,6 @@ - @@ -399,6 +403,19 @@ + + + + + + + + + + + + + @@ -477,6 +494,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -488,13 +534,8 @@ - - - - - - - + + @@ -687,6 +728,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -698,13 +768,8 @@ - - - - - - - + + @@ -1944,6 +2009,9 @@ + + + @@ -1952,9 +2020,6 @@ - - - @@ -4337,37 +4402,16 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - + + @@ -4590,31 +4634,52 @@ - - - - - - + + + + + + + + - - - - + + + + - + - - + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.fbnetwork.subapp.mps b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.fbnetwork.subapp.mps index 450e913cd..db2281cd7 100644 --- a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.fbnetwork.subapp.mps +++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.fbnetwork.subapp.mps @@ -134,7 +134,6 @@ - @@ -364,41 +363,20 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps index a50c13f3a..27717cb54 100644 --- a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps +++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.interfacepart.mps @@ -146,6 +146,7 @@ + @@ -2221,7 +2222,7 @@ - + @@ -6236,13 +6237,13 @@ - + - + @@ -6326,37 +6327,56 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6402,26 +6422,47 @@ - - - - + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + @@ -6481,29 +6522,46 @@ - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + - - - - - + + + + + @@ -7087,7 +7145,7 @@ - + diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.st.mps b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.st.mps index 5985c80d3..c7889f438 100644 --- a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.st.mps +++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.st.mps @@ -462,7 +462,7 @@ - + @@ -1015,7 +1015,7 @@ - + @@ -1106,7 +1106,7 @@ - + @@ -1385,7 +1385,7 @@ - + @@ -1634,6 +1634,19 @@ + + + + + + + + + + + + + @@ -1928,6 +1941,28 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -2322,6 +2357,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2387,7 +2454,7 @@ - + @@ -2480,7 +2547,7 @@ - + @@ -2614,7 +2681,7 @@ - + @@ -2820,7 +2887,7 @@ - + @@ -3154,7 +3221,7 @@ - + @@ -3606,7 +3673,7 @@ - + @@ -3697,7 +3764,7 @@ - + @@ -3788,7 +3855,7 @@ - + @@ -5260,7 +5327,7 @@ - + @@ -5468,7 +5535,7 @@ - + @@ -5650,7 +5717,7 @@ - + @@ -5893,7 +5960,7 @@ - + @@ -6026,7 +6093,7 @@ - + diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.repository.mps b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.repository.mps index 957bb7be1..7b30eee98 100644 --- a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.repository.mps +++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.repository.mps @@ -3015,6 +3015,29 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformDeclarationsScope.kt b/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformDeclarationsScope.kt index a0c3f5053..ba62b7a53 100644 --- a/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformDeclarationsScope.kt +++ b/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformDeclarationsScope.kt @@ -10,11 +10,12 @@ import org.jetbrains.mps.openapi.model.SNode import org.jetbrains.mps.openapi.model.SNodeReference internal class PlatformDeclarationsScope( - private val myRepository: PlatformRepository, - private val myModel: SModel? + private val repository: PlatformRepository, + private val model: SModel?, ) : DeclarationsScope { + override fun findCompositeFBTypeDeclaration(identifier: Identifier): CompositeFBTypeDeclaration? { - return findNode(identifier)?.let { myRepository.getAdapter(it, CompositeFBTypeDeclaration::class.java) } + return findNode(identifier)?.let { repository.adapterOrNull(it) } } override fun findCATBlockTypeDeclaration(identifier: Identifier): CATBlockTypeDeclaration? { @@ -22,56 +23,56 @@ internal class PlatformDeclarationsScope( } override fun findBasicFBTypeDeclaration(identifier: Identifier): BasicFBTypeDeclaration? { - return findNode(identifier)?.let { myRepository.getAdapter(it, BasicFBTypeDeclaration::class.java) } + return findNode(identifier)?.let { repository.adapterOrNull(it) } } override fun findServiceFBTypeDeclaration(identifier: Identifier): ServiceInterfaceFBTypeDeclaration? { - return findNode(identifier)?.let { myRepository.getAdapter(it, ServiceInterfaceFBTypeDeclaration::class.java) } + return findNode(identifier)?.let { repository.adapterOrNull(it) } } override fun findAdapterTypeDeclaration(identifier: Identifier): AdapterTypeDeclaration? { - return findNode(identifier)?.let { myRepository.getAdapter(it, AdapterTypeDeclaration::class.java) } + return findNode(identifier)?.let { repository.adapterOrNull(it) } } override fun findSubapplicationTypeDeclaration(identifier: Identifier): SubapplicationTypeDeclaration? { - return findNode(identifier)?.let { myRepository.getAdapter(it, SubapplicationTypeDeclaration::class.java) } + return findNode(identifier)?.let { repository.adapterOrNull(it) } } override fun findSystemDeclaration(identifier: Identifier): SystemDeclaration? { - return findNode(identifier)?.let { myRepository.getAdapter(it, SystemDeclaration::class.java) } + return findNode(identifier)?.let { repository.adapterOrNull(it) } } override fun findDeviceDeclaration(identifier: Identifier): DeviceDeclaration? { - return findNode(identifier)?.let { myRepository.getAdapter(it, DeviceDeclaration::class.java) } + return findNode(identifier)?.let { repository.adapterOrNull(it) } } override fun findResourceDeclaration(identifier: Identifier): ResourceDeclaration? { - return findNode(identifier)?.let { myRepository.getAdapter(it, ResourceDeclaration::class.java) } + return findNode(identifier)?.let { repository.adapterOrNull(it) } } override fun findFunctionBlockDeclaration(identifier: Identifier): FunctionBlockDeclaration? { - return findNode(identifier)?.let { myRepository.getAdapter(it, FunctionBlockDeclaration::class.java) } + return findNode(identifier)?.let { repository.adapterOrNull(it) } } override fun findAllFBTypeDeclarations(): List { - return myRepository.mpsRepository.modules + return repository.mpsRepository.modules .flatMap { it.models } .filter { - myModel == null || - myModel.reference == it.reference || - ModelImports(myModel).importedModels.contains(it.reference) + model == null || + model.reference == it.reference || + ModelImports(model).importedModels.contains(it.reference) } .flatMap { it.rootNodes } - .mapNotNull { myRepository.getAdapter(it, FBTypeDeclaration::class.java) } + .mapNotNull { repository.adapterOrNull(it) } } private fun findNode(identifier: Identifier): SNode? { - val node = getNodeReference(identifier).resolve(myRepository.mpsRepository) ?: return null - if (myModel == null) { - return node - } + val node = getNodeReference(identifier).resolve(repository.mpsRepository) ?: return null + + model ?: return node + val reference = requireNotNull(node.model).reference - return node.takeIf { myModel.reference == reference || ModelImports(myModel).importedModels.contains(reference) } + return node.takeIf { model.reference == reference || ModelImports(model).importedModels.contains(reference) } } private fun getNodeReference(identifier: Identifier): SNodeReference { diff --git a/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformElementsOwner.kt b/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformElementsOwner.kt index 12ad891fb..cf1dcaa7c 100644 --- a/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformElementsOwner.kt +++ b/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformElementsOwner.kt @@ -1,5 +1,6 @@ package org.fbme.ide.iec61499.repository +import com.intellij.util.castSafelyTo import org.fbme.lib.common.Element import org.fbme.lib.iec61499.IEC61499Factory import org.fbme.lib.st.STFactory @@ -10,14 +11,23 @@ open class PlatformElementsOwner { private val elements: MutableMap = HashMap() private val adapter: PlatformElementAdapter = MpsBridge.createElementAdapter(this) - fun getAdapter(node: SNode, requiredClass: Class): T { - val adapter: Any? = elements.computeIfAbsent(node) { adapt(it) } - if (adapter == null) { - elements.remove(node) - } - return requiredClass.cast(adapter) + fun getAdapterRaw(node: SNode): Element? { + val adapter = elements.computeIfAbsent(node) { adapt(it) } + if (adapter == null) elements.remove(node) + return adapter } + fun getAdapterNullable(node: SNode?, requiredClass: Class): T? = + node?.let { getAdapterRaw(it) } + ?.takeIf { requiredClass.isInstance(it) } + ?.let { requiredClass.cast(it) } + + fun getAdapter(node: SNode, requiredClass: Class): T = + requiredClass.cast(getAdapterRaw(node)) + + inline fun adapter(node: SNode) = getAdapterRaw(node) as T + inline fun adapterOrNull(node: SNode) = getAdapterRaw(node) as? T + private fun adapt(node: SNode): Element? { return adapter.adapt(node) } diff --git a/code/library/build.gradle.kts b/code/library/build.gradle.kts index 0f55aaecb..27eb8138d 100644 --- a/code/library/build.gradle.kts +++ b/code/library/build.gradle.kts @@ -23,7 +23,6 @@ configurations { } mps { - buildScriptName.set("fbme_library") moduleName.set("org.fbme.lib") } diff --git a/code/library/src/main/antlr/org/fbme/lib/st/parser/ST.g4 b/code/library/src/main/antlr/org/fbme/lib/st/parser/ST.g4 index 91d0cf9ad..00b3ecfce 100644 --- a/code/library/src/main/antlr/org/fbme/lib/st/parser/ST.g4 +++ b/code/library/src/main/antlr/org/fbme/lib/st/parser/ST.g4 @@ -60,15 +60,17 @@ expression ; literal - : DecInteger #dec - | BinInteger #bin - | OctInteger #oct - | HexInteger #hex - | String #string - | WString #wstring - | Boolean #boolean - | BooleanBin #booleanBin - | Duration #duration + : SingedInteger #dec + | DecInteger #dec + | BinInteger #bin + | OctInteger #oct + | HexInteger #hex + | Real #real + | String #string + | WString #wstring + | Boolean #boolean + | BooleanBin #booleanBin + | Duration #duration ; variable @@ -93,6 +95,11 @@ BinInteger: '2#' Bin ('_'|Bin)*; OctInteger: '8#' Oct ('_'|Oct)*; HexInteger: '16#' Hex ('_'|Hex)*; +SingedInteger: ('+'|'-')? DecInteger; + +fragment Exponent: ('e'|'E') SingedInteger; +Real: SingedInteger '.' DecInteger Exponent?; + String: '\'' (('$' [$'LNPRT] | '$' Hex Hex)| ~['\n\r\\])* '\''; WString: '"' (('$' [$"LNPRT] | '$' Hex Hex Hex Hex)| ~["\n\r\\])* '"'; @@ -101,14 +108,14 @@ BooleanBin: 'BOOL#'('1'|'0'); Duration: ('T' | 'TIME') '#' '-'? Interval; -Interval: Days | Hours | Minutes | Seconds | Milliseconds; -FixedPoint: DecInteger ( '.' DecInteger)?; +fragment Interval: Days | Hours | Minutes | Seconds | Milliseconds; +fragment FixedPoint: DecInteger ( '.' DecInteger)?; -Days: FixedPoint 'd' | DecInteger 'd' '_' Hours; -Hours: FixedPoint 'h' | DecInteger 'h' '_' Minutes; -Minutes: FixedPoint 'm' | DecInteger 'm' '_' Seconds; -Seconds: FixedPoint 's' | DecInteger 's' '_' Milliseconds; -Milliseconds: FixedPoint 'ms'; +fragment Days: FixedPoint 'd' | DecInteger 'd' '_' Hours; +fragment Hours: FixedPoint 'h' | DecInteger 'h' '_' Minutes; +fragment Minutes: FixedPoint 'm' | DecInteger 'm' '_' Seconds; +fragment Seconds: FixedPoint 's' | DecInteger 's' '_' Milliseconds; +fragment Milliseconds: FixedPoint 'ms'; ID: [A-Za-z][A-Za-z_0-9]*; diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/hierarchies/ResourceFunctionBlockHierarchy.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/hierarchies/ResourceFunctionBlockHierarchy.kt index 8d5b8ac85..4080a5937 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/hierarchies/ResourceFunctionBlockHierarchy.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/declarations/hierarchies/ResourceFunctionBlockHierarchy.kt @@ -6,8 +6,8 @@ import org.fbme.lib.iec61499.fbnetwork.FunctionBlockDeclaration class ResourceFunctionBlockHierarchy( val resourceHierarchy: ResourceHierarchy, - val functionBlock: FunctionBlockDeclaration + val functionBlock: FunctionBlockDeclaration? ) : DeclarationPath { override val declarations: List - get() = listOf(resourceHierarchy.device, resourceHierarchy.resource, functionBlock) + get() = listOfNotNull(resourceHierarchy.device, resourceHierarchy.resource, functionBlock) } diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/BasicFBTypeConverter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/BasicFBTypeConverter.kt index 56a560e9f..8ec670cde 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/BasicFBTypeConverter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/BasicFBTypeConverter.kt @@ -92,8 +92,9 @@ open class BasicFBTypeConverter(arguments: ConverterArguments) : if (openBracketIndex > 0) { condition.eventReference.setFQName(rawCondition.substring(0, openBracketIndex)) } - val guardConditionText = unescapeXML(rawCondition.substring(openBracketIndex + 1, closeBracketIndex)) - condition.setGuardCondition(STConverter.parseExpression(stFactory, guardConditionText)!!) + val guardConditionText = rawCondition.substring(openBracketIndex + 1, closeBracketIndex).unescapeXML() + val expression = STConverter.parseExpression(stFactory, guardConditionText) ?: return + condition.setGuardCondition(expression) } fun interface StAlgorithmConverter { @@ -133,7 +134,7 @@ open class BasicFBTypeConverter(arguments: ConverterArguments) : if (stBodyElement != null) { val st = factory.createAlgorithmBody(AlgorithmLanguage.ST) algorithmDeclaration.body = st - val stText = unescapeXML(stBodyElement.getAttributeValue("Text")) + val stText = stBodyElement.getAttributeValue("Text")?.unescapeXML() if (stText != null) { stAlgorithmConverter.convert(factory, stFactory, algorithmDeclaration, st, stText) } @@ -143,7 +144,7 @@ open class BasicFBTypeConverter(arguments: ConverterArguments) : val unknown = factory.createAlgorithmBody(AlgorithmLanguage.unknown(otherBodyElement.getAttributeValue("Language"))) algorithmDeclaration.body = unknown - val text = unescapeXML(otherBodyElement.getAttributeValue("Text")) + val text = otherBodyElement.getAttributeValue("Text")?.unescapeXML() if (text != null) { unknown.text = text } diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/ConverterBase.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/ConverterBase.kt index e0e7b8945..b230a2b80 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/ConverterBase.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/ConverterBase.kt @@ -7,9 +7,8 @@ abstract class ConverterBase(arguments: ConverterArguments) : ConverterArguments final override val identifierLocus = arguments.identifierLocus final override val element = arguments.element - protected fun unescapeXML(text: String?): String? { - text ?: return null - return text + protected fun String.unescapeXML(): String { + return this .replace(" ", "\n") .replace(""", "\"") .replace("&", "&") diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/ParameterAssignmentsConverter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/ParameterAssignmentsConverter.kt index acc0140d6..7506e0560 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/ParameterAssignmentsConverter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/ParameterAssignmentsConverter.kt @@ -12,9 +12,10 @@ class ParameterAssignmentsConverter( for (parameterElement in parameterElements) { val parameter = factory.createParameterAssignment() parameter.parameterReference.setTargetName(parameterElement.getAttributeValue("Name")) - val valueText = unescapeXML(parameterElement.getAttributeValue("Value")) - parameter.value = STConverter.parseLiteral(stFactory, valueText) assignments.add(parameter) + val valueText = parameterElement.getAttributeValue("Value")?.unescapeXML() + valueText ?: continue + parameter.value = STConverter.parseLiteral(stFactory, valueText) } } } diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/STConverter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/STConverter.kt index a777a423e..ba0eadf47 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/STConverter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/STConverter.kt @@ -32,17 +32,17 @@ object STConverter { } @JvmStatic - fun parseStatementList(factory: STFactory, text: String?): List { + fun parseStatementList(factory: STFactory, text: String): List { return extractStatementList(factory, createParser(text).statementList()) } @JvmStatic - fun parseExpression(factory: STFactory, text: String?): Expression? { + fun parseExpression(factory: STFactory, text: String): Expression? { return extractExpression(factory, createParser(text).expression()) } @JvmStatic - fun parseLiteral(factory: STFactory, text: String?): Literal<*>? { + fun parseLiteral(factory: STFactory, text: String): Literal<*>? { return extractLiteral(factory, createParser(text).literal()) } @@ -146,7 +146,7 @@ object STConverter { } else null } - private fun extractExpression(factory: STFactory, expressionCtx: ExpressionContext): Expression? { + private fun extractExpression(factory: STFactory, expressionCtx: ExpressionContext?): Expression? { if (expressionCtx is ConstantContext) { return extractLiteral(factory, expressionCtx.literal()) } @@ -203,7 +203,7 @@ object STConverter { return null } - private fun extractVariable(factory: STFactory, variableCtx: VariableContext): Variable? { + private fun extractVariable(factory: STFactory, variableCtx: VariableContext?): Variable? { if (variableCtx is VarReferenceContext) { val variableReference = factory.createVariableReference() variableReference.reference.setTargetName(variableCtx.getText()) @@ -223,7 +223,7 @@ object STConverter { return null } - private fun extractLiteral(factory: STFactory, literalCtx: LiteralContext): Literal<*>? { + private fun extractLiteral(factory: STFactory, literalCtx: LiteralContext?): Literal<*>? { if (literalCtx is DecContext) { val literal = factory.createLiteral(LiteralKind.DEC_INT) as Literal literal.value = literalCtx.text.toInt() @@ -273,10 +273,15 @@ object STConverter { literal.value = literalCtx.text.substringAfter('#') return literal } + if (literalCtx is RealContext) { + val literal = factory.createLiteral(LiteralKind.REAL) as Literal + literal.value = literalCtx.text + return literal + } return null } - private fun createParser(text: String?): STParser { + private fun createParser(text: String): STParser { return try { STParser(CommonTokenStream(STLexer(ANTLRInputStream(StringReader(text))))) } catch (e: IOException) { diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/ParameterAssignmentPrinter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/ParameterAssignmentPrinter.kt index 8538b1427..228aa357a 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/ParameterAssignmentPrinter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/ParameterAssignmentPrinter.kt @@ -7,7 +7,10 @@ class ParameterAssignmentPrinter(assignment: ParameterAssignment) : PrinterElementBase(assignment, "Parameter") { override fun printElementBody(element: Element) { element.setAttribute("Name", this.element.parameterReference.presentation) - element.setAttribute("Value", STPrinter.printLiteral(requireNotNull(this.element.value))) + val value = this.element.value + if (value != null) { + element.setAttribute("Value", STPrinter.printLiteral(value)) + } } companion object { diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/STPrinter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/STPrinter.kt index 1914d1f5c..7e8252908 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/STPrinter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/STPrinter.kt @@ -2,7 +2,6 @@ package org.fbme.lib.iec61499.stringify import org.fbme.lib.st.expressions.* import org.fbme.lib.st.statements.* -import java.lang.Boolean import kotlin.IllegalArgumentException import kotlin.Int import kotlin.String @@ -33,50 +32,52 @@ class STPrinter { } private fun appendExpression(expression: Expression?) { - if (expression is BinaryExpression) { - appendExpression(expression.leftExpression) - append(" ") - append(expression.operation.alias) - append(" ") - appendExpression(expression.rightExpression) - return - } - if (expression is UnaryExpression) { - val operation = expression.operation - append(operation.alias) - append(if (operation.isSpaced) " " else "") - appendExpression(expression.getInnerExpression()) - return - } - if (expression is FunctionCall) { - append(expression.functionName) - append("(") - val actualParameters = expression.actualParameters - val last = actualParameters[actualParameters.size - 1] - for (actualParameter in actualParameters) { - appendExpression(actualParameter) - if (actualParameter !== last) { - append(", ") + expression ?: return + + when (expression) { + is BinaryExpression -> { + appendExpression(expression.leftExpression) + append(" ") + append(expression.operation.alias) + append(" ") + appendExpression(expression.rightExpression) + } + + is UnaryExpression -> { + val operation = expression.operation + append(operation.alias) + append(if (operation.isSpaced) " " else "") + appendExpression(expression.getInnerExpression()) + } + + is FunctionCall -> { + append(expression.functionName) + append("(") + val actualParameters = expression.actualParameters + val last = actualParameters[actualParameters.size - 1] + for (actualParameter in actualParameters) { + appendExpression(actualParameter) + if (actualParameter !== last) { + append(", ") + } } + append(")") + } + + is ParenthesisExpression -> { + append("(") + appendExpression(expression.innerExpression) + append(")") + } + + is Variable -> { + appendVariable(expression as Variable?) + } + + is Literal<*> -> { + appendLiteral(expression) } - append(")") - return - } - if (expression is ParenthesisExpression) { - append("(") - appendExpression(expression.innerExpression) - append(")") - return - } - if (expression is Variable) { - appendVariable(expression as Variable?) - return - } - if (expression is Literal<*>) { - appendLiteral(expression) - return } - throw IllegalArgumentException("Unrecognized expression $expression") } private fun appendVariable(variable: Variable?) { @@ -104,15 +105,16 @@ class STPrinter { private fun appendLiteral(literal: Literal<*>) { val value = literal.value when (literal.kind) { - LiteralKind.BINARY_BOOL -> append(if (value === Boolean.TRUE) "BOOL#1" else "BOOL#0") + LiteralKind.BINARY_BOOL -> append(if (value == true) "BOOL#1" else "BOOL#0") LiteralKind.BINARY_INT -> append("2#" + (value as Int).toString(2)) - LiteralKind.BOOL -> append(if (value === Boolean.TRUE) "TRUE" else "FALSE") + LiteralKind.BOOL -> append(if (value == true) "TRUE" else "FALSE") LiteralKind.DEC_INT -> append((value as Int).toString()) LiteralKind.HEX_INT -> append("16#" + (value as Int).toString(16)) LiteralKind.OCT_INT -> append("8#" + (value as Int).toString(8)) LiteralKind.STRING -> append("'$value'") LiteralKind.WSTRING -> append("\"" + value + "\"") LiteralKind.TIME -> append("T#$value") + LiteralKind.REAL -> append(value.toString()) else -> throw IllegalArgumentException("Unrecognized literal $literal") } } @@ -129,14 +131,14 @@ class STPrinter { private fun appendStatement(statement: Statement) { appendIndent() - try { - if (statement is AssignmentStatement) { + when (statement) { + is AssignmentStatement -> { appendVariable(statement.variable) append(" := ") appendExpression(statement.expression) - return } - if (statement is CaseStatement) { + + is CaseStatement -> { append("CASE ") appendExpression(statement.expression) append(" OF ") @@ -158,16 +160,17 @@ class STPrinter { } appendIndent() append("END_CASE") - return } - if (statement is EmptyStatement) { + + is EmptyStatement -> { return } - if (statement is ExitStatement) { + + is ExitStatement -> { append("EXIT") - return } - if (statement is ForStatement) { + + is ForStatement -> { val controlVariable = statement.controlVariable append("FOR ") append(controlVariable.name) @@ -186,7 +189,8 @@ class STPrinter { appendIndent() append("END_FOR") } - if (statement is IfStatement) { + + is IfStatement -> { append("IF ") appendExpression(statement.condition) append(" THEN") @@ -210,7 +214,8 @@ class STPrinter { appendIndent() append("END_IF") } - if (statement is RepeatStatement) { + + is RepeatStatement -> { append("REPEAT") appendNewLine() withIndent { appendStatementList(statement.body) } @@ -219,10 +224,12 @@ class STPrinter { appendExpression(statement.condition) append(" END_REPEAT") } - if (statement is ReturnStatement) { + + is ReturnStatement -> { append("RETURN") } - if (statement is WhileStatement) { + + is WhileStatement -> { append("WHILE ") appendExpression(statement.condition) append(" DO") @@ -231,10 +238,9 @@ class STPrinter { appendIndent() append(" END_WHILE") } - } finally { - append(";") - appendNewLine() } + append(";") + appendNewLine() } override fun toString(): String { diff --git a/code/library/src/main/kotlin/org/fbme/lib/st/expressions/LiteralKind.kt b/code/library/src/main/kotlin/org/fbme/lib/st/expressions/LiteralKind.kt index 421ab599e..c21ac81d9 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/st/expressions/LiteralKind.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/st/expressions/LiteralKind.kt @@ -1,5 +1,5 @@ package org.fbme.lib.st.expressions enum class LiteralKind { - BINARY_BOOL, BINARY_INT, BOOL, DEC_INT, HEX_INT, OCT_INT, STRING, WSTRING, TIME + BINARY_BOOL, BINARY_INT, BOOL, DEC_INT, HEX_INT, OCT_INT, STRING, WSTRING, TIME, REAL } diff --git a/code/nxt-integration/build.gradle.kts b/code/nxt-integration/build.gradle.kts index 9c4077463..fb03f2695 100644 --- a/code/nxt-integration/build.gradle.kts +++ b/code/nxt-integration/build.gradle.kts @@ -11,6 +11,11 @@ dependencies { compileOnly(project(":code:language")) compileOnly(project(":code:platform")) + testImplementation(mpsDistribution()) + testImplementation(project(":code:library")) + testImplementation(project(":code:language")) + testImplementation(project(":code:platform")) + mpsImplementation(project(":code:library", "mps")) mpsImplementation(project(":code:language", "mps")) mpsImplementation(project(":code:platform", "mps")) diff --git a/code/nxt-integration/src/main/kotlin/org/fbme/integration/nxt/importer/BasicFbTypeNxtImporter.kt b/code/nxt-integration/src/main/kotlin/org/fbme/integration/nxt/importer/BasicFbTypeNxtImporter.kt index 510459031..27bdb7226 100644 --- a/code/nxt-integration/src/main/kotlin/org/fbme/integration/nxt/importer/BasicFbTypeNxtImporter.kt +++ b/code/nxt-integration/src/main/kotlin/org/fbme/integration/nxt/importer/BasicFbTypeNxtImporter.kt @@ -10,6 +10,7 @@ import org.fbme.lib.iec61499.parser.STConverter.parseExpression import org.fbme.lib.iec61499.parser.STConverter.parseStatementListWithDeclarations import org.fbme.lib.st.STFactory import org.fbme.lib.st.expressions.* +import org.jetbrains.annotations.NotNull class BasicFbTypeNxtImporter(arguments: ConverterArguments) : BasicFBTypeConverter(arguments) { override fun parseCondition( @@ -25,7 +26,7 @@ class BasicFbTypeNxtImporter(arguments: ConverterArguments) : BasicFBTypeConvert val openBracketIndex = rawCondition.indexOf('[') val closeBracketIndex = rawCondition.lastIndexOf(']') if (openBracketIndex == -1) { - val guardCondition = parseExpression(stFactory, unescapeXML(rawCondition)) + val guardCondition = parseExpression(stFactory, rawCondition.unescapeXML()) val checker = TransitionImportChecker(fbtd) checker.checkTransition(guardCondition) if (checker.satisfies()) { @@ -43,7 +44,7 @@ class BasicFbTypeNxtImporter(arguments: ConverterArguments) : BasicFBTypeConvert if (openBracketIndex > 0) { condition.eventReference.setFQName(rawCondition.substring(0, openBracketIndex)) } - val guardConditionText = unescapeXML(rawCondition.substring(openBracketIndex + 1, closeBracketIndex)) + val guardConditionText = rawCondition.substring(openBracketIndex + 1, closeBracketIndex).unescapeXML() condition.setGuardCondition(parseExpression(stFactory, guardConditionText)!!) } @@ -118,6 +119,9 @@ class BasicFbTypeNxtImporter(arguments: ConverterArguments) : BasicFBTypeConvert } } + @NotNull + override val stAlgorithmConverter = Companion.stAlgorithmConverter + companion object { private val stAlgorithmConverter: StAlgorithmConverter = StAlgorithmConverter { iec61499factory: IEC61499Factory, factory: STFactory, algorithmDeclaration: AlgorithmDeclaration, st: AlgorithmBody.ST, text: String -> diff --git a/code/nxt-integration/src/main/kotlin/org/fbme/integration/nxt/importer/NxtProjectTemplate.kt b/code/nxt-integration/src/main/kotlin/org/fbme/integration/nxt/importer/NxtProjectTemplate.kt index 2d800f86b..1616dfdb7 100644 --- a/code/nxt-integration/src/main/kotlin/org/fbme/integration/nxt/importer/NxtProjectTemplate.kt +++ b/code/nxt-integration/src/main/kotlin/org/fbme/integration/nxt/importer/NxtProjectTemplate.kt @@ -1,12 +1,13 @@ package org.fbme.integration.nxt.importer +import com.intellij.notification.Notification +import com.intellij.notification.NotificationType +import com.intellij.notification.Notifications +import com.intellij.openapi.project.Project import fbme.integration.nxt.NxtIntegrationIcons import jetbrains.mps.extapi.model.SModelSimpleHeader -import jetbrains.mps.internal.collections.runtime.ListSequence -import jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations import jetbrains.mps.smodel.SModelId import jetbrains.mps.smodel.SNodeUtil -import jetbrains.mps.util.FileUtil import jetbrains.mps.util.JDOMUtil import jetbrains.mps.util.NameUtil import org.fbme.ide.iec61499.repository.PlatformElement @@ -25,8 +26,8 @@ import org.jetbrains.mps.openapi.persistence.PersistenceFacade import java.io.BufferedReader import java.io.File import java.io.FileReader -import java.io.IOException import java.util.* +import kotlin.collections.ArrayDeque class NxtImportProjectTemplate : Iec61499ProjectTemplate( @@ -36,21 +37,34 @@ class NxtImportProjectTemplate : Iec61499ProjectTemplate( NxtIntegrationIcons.importProject, null ) { - override fun initModel(repository: PlatformRepository, model: SModel): PlatformElement { + override fun initModel(project: Project, repository: PlatformRepository, model: SModel): PlatformElement { val settings = settings as NxtImportSystemConfigSolutionSettings val nxtImportDirectory = settings.getNxtImportLocation() val modelId = SModelId.generate() val modelName = settings.moduleName val ref = PersistenceFacade.getInstance().createModelReference(null, modelId, modelName) val header: SModelSimpleHeader = Iec61499ModelHeader(ref, emptyList()) - val files = listOf(*File(nxtImportDirectory).listFiles()!!) - try { - readModel(header, files, model) - } catch (e: ModelLoadException) { + val entries = loadEntries(locateRootDirectory(File(nxtImportDirectory))) + val errorEntries = mutableSetOf() + for (entry in entries) { + try { + loadRootFromFile(header, entry, model) + } catch (e: Exception) { + errorEntries += entry + } + } + if (errorEntries.isNotEmpty()) { + val notification = Notification( + "fbme.integration.nxt", + "Error during import", + "Failed to load ${errorEntries.size} documents: ${errorEntries.joinToString { it.name }}", + NotificationType.ERROR + ) + Notifications.Bus.notify(notification, project) } val first = model.rootNodes.firstOrNull() if (first != null) { - return repository.getAdapter(first, PlatformElement::class.java) + return repository.adapter(first) } val result = repository.iec61499Factory.createBasicFBTypeDeclaration(null) result.name = "EmptyBasicFB" @@ -58,33 +72,17 @@ class NxtImportProjectTemplate : Iec61499ProjectTemplate( } @Throws(ModelLoadException::class) - fun readModel(header: SModelSimpleHeader, files: List, m: SModel) { - try { - for (file in ListSequence.fromList(files)) { - try { - val fileExt = FileUtil.getExtension(file.name) - val fqName = - com.intellij.openapi.util.io.FileUtil.getNameWithoutExtension(file).replace('/', '.') - if (!supportedFileExtension(fileExt)) { - continue - } - BufferedReader(FileReader(file)).use { reader -> - val doc = JDOMUtil.loadDocument(reader) - val node = convertRootNode(header.modelReference, doc, fileExt) - if (node != null) { - val virtualPackage = NameUtil.namespaceFromLongName(fqName) - if (virtualPackage != null && virtualPackage.isNotEmpty()) { - node.setProperty(SNodeUtil.property_BaseConcept_virtualPackage, virtualPackage) - } - SModelOperations.addRootNode(m, node) - } - } - } catch (e: Exception) { - throw IOException("cannot read model for nxt project import", e) + fun loadRootFromFile(header: SModelSimpleHeader, entry: DocumentEntry, model: SModel) { + BufferedReader(FileReader(entry.file)).use { reader -> + val doc = JDOMUtil.loadDocument(reader) + val node = convertRootNode(header.modelReference, doc, entry.file.extension) + if (node != null) { + val virtualPackage = NameUtil.namespaceFromLongName(entry.name) + if (virtualPackage != null && virtualPackage.isNotEmpty()) { + node.setProperty(SNodeUtil.property_BaseConcept_virtualPackage, virtualPackage) } + model.addRootNode(node) } - } catch (e: IOException) { - throw ModelLoadException("Exception during load", emptyList(), e) } } @@ -104,7 +102,7 @@ class NxtImportProjectTemplate : Iec61499ProjectTemplate( } } - private fun supportedFileExtension(fileExt: String?): Boolean { + private fun supportedFileExtension(fileExt: String): Boolean { return fileExt == Iec61499ModelFactory.FBT_FILE_EXT || fileExt == Iec61499ModelFactory.ADP_FILE_EXT || fileExt == Iec61499ModelFactory.SUB_FILE_EXT @@ -113,4 +111,31 @@ class NxtImportProjectTemplate : Iec61499ProjectTemplate( || fileExt == Iec61499ModelFactory.SYS_FILE_EXT || fileExt == Iec61499ModelFactory.SEG_FILE_EXT } + + private fun locateRootDirectory(nxtImportDirectory: File): File { + val files = nxtImportDirectory.list() ?: return nxtImportDirectory + if (files.contains("IEC61499") && files.any { it.endsWith("sln") }) { + return nxtImportDirectory.resolve("IEC61499") + } + return nxtImportDirectory + } + + private fun loadEntries(rootDirectory: File): Sequence = sequence { + val queue = ArrayDeque() + queue.add(DocumentEntry("", rootDirectory)) + while (queue.isNotEmpty()) { + val entry = queue.removeFirst() + if (entry.file.isFile && supportedFileExtension(entry.file.extension)) yield(entry) + queue.addAll(entry.nestedDocuments) + } + } + + + data class DocumentEntry(val name: String, val file: File) { + + private fun nestedName(nestedName: String) = if (name.isEmpty()) nestedName else "$name.$nestedName" + + val nestedDocuments + get() = file.listFiles()?.map { DocumentEntry(nestedName(it.nameWithoutExtension), it) } ?: emptyList() + } } diff --git a/code/nxt-integration/src/test/kotlin/org/fbme/integration/nxt/importer/BasicFbTypeNxtImporterTest.kt b/code/nxt-integration/src/test/kotlin/org/fbme/integration/nxt/importer/BasicFbTypeNxtImporterTest.kt new file mode 100644 index 000000000..5ed04f52e --- /dev/null +++ b/code/nxt-integration/src/test/kotlin/org/fbme/integration/nxt/importer/BasicFbTypeNxtImporterTest.kt @@ -0,0 +1,30 @@ +package org.fbme.integration.nxt.importer + +import org.fbme.ide.platform.testing.PlatformTestBase +import org.fbme.ide.platform.testing.PlatformTestRunner +import org.fbme.lib.iec61499.declarations.BasicFBTypeDeclaration +import org.fbme.lib.st.types.ElementaryType +import org.junit.Assert +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(PlatformTestRunner::class) +class BasicFbTypeNxtImporterTest : PlatformTestBase() { + @Test + fun parseTest1() { + val fbType = rootConverterByPath("/boolMerge.fbt", NxtImporterConfiguration.FACTORY).convertFBType() + Assert.assertNotNull(fbType) + } + + @Test + fun testParseAlgorithmWithTemporaryVariables() { + val fbType = rootConverterByPath("/AlgorithmWithTemporaryVariables.fbt", NxtImporterConfiguration.FACTORY).convertFBType() + fbType as BasicFBTypeDeclaration + val algorithm = fbType.algorithms[0] + Assert.assertNotNull(algorithm) + val temporaryVariable = algorithm.temporaryVariables[0] + Assert.assertNotNull(temporaryVariable) + Assert.assertEquals("temp", temporaryVariable.name) + Assert.assertEquals(ElementaryType.INT, temporaryVariable.type) + } +} diff --git a/code/platform/build.gradle.kts b/code/platform/build.gradle.kts index 16f04b98e..8024bb0de 100644 --- a/code/platform/build.gradle.kts +++ b/code/platform/build.gradle.kts @@ -13,12 +13,14 @@ dependencies { implementation(project(":code:language")) + testImplementation(mpsDistribution()) + testImplementation(project(":code:library")) + mpsImplementation(project(":code:library", "mps")) mpsImplementation(project(":code:language", "mps")) } mps { - buildScriptName.set("fbme_platform") moduleName.set("org.fbme.platform.lib") includeMpsArtifacts(project(":code:language")) moduleDependency(project(":code:library")) diff --git a/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.mps b/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.mps deleted file mode 100644 index b58d68bd8..000000000 --- a/code/platform/solutions/org.fbme.ide.platform/models/org.fbme.ide.platform.mps +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/code/platform/solutions/org.fbme.ide.platform/org.fbme.ide.platform.msd b/code/platform/solutions/org.fbme.ide.platform/org.fbme.ide.platform.msd deleted file mode 100644 index e639527d5..000000000 --- a/code/platform/solutions/org.fbme.ide.platform/org.fbme.ide.platform.msd +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - 6594f340-4d73-4027-b7d3-c6ca2e70a53b(org.fbme.ide.iec61499.lang) - 498d89d2-c2e9-11e2-ad49-6cf049e62fe5(MPS.IDEA) - ceab5195-25ea-4f22-9b92-103b95ca8c0c(jetbrains.mps.lang.core) - 6354ebe7-c22a-4a0f-ac54-50b52ab9b065(JDK) - 8865b7a8-5271-43d3-884c-6fd1d9cfdd34(MPS.OpenAPI) - 742f6602-5a2f-4313-aa6e-ae1cd4ffdc61(MPS.Platform) - 1db6de07-b355-4c0f-9979-75b4ac1e8215(org.fbme.lib) - 86441d7a-e194-42da-81a5-2161ec62a379(MPS.Workbench) - 1ed103c3-3aa6-49b7-9c21-6765ee11f224(MPS.Editor) - 25092e07-e655-497c-92fb-558a8e3080ed(jetbrains.mps.ide.ui) - 6ed54515-acc8-4d1e-a16c-9fd6cfe951ea(MPS.Core) - 3f233e7f-b8a6-46d2-a57f-795d56775243(Annotations) - ce018f97-56b9-4ee7-9b5f-2d462b6628bf(org.fbme.platform.lib) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/debugger/DeviceConnection.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/debugger/DeviceConnection.kt index a00a2ec5f..6868f4524 100644 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/debugger/DeviceConnection.kt +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/debugger/DeviceConnection.kt @@ -1,37 +1,33 @@ package org.fbme.ide.platform.debugger import org.fbme.lib.iec61499.declarations.ResourceDeclaration -import org.jetbrains.mps.openapi.model.SNode import java.io.Closeable import java.io.IOException interface DeviceConnection : Closeable { val isAlive: Boolean - @Throws(IOException::class) - fun deployResource(resource: SNode) - @Throws(IOException::class) fun deployResource(resource: ResourceDeclaration) @Throws(IOException::class) - fun killResource(resource: SNode) + fun killResource(resource: ResourceDeclaration) @Throws(IOException::class) - fun deleteResource(resource: SNode) + fun deleteResource(resource: ResourceDeclaration) @Throws(IOException::class) - fun addWatch(watchable: Watchable) + fun createResourceNetwork(resource: ResourceDeclaration) @Throws(IOException::class) - fun removeWatch(watchable: Watchable) + fun startResource(resource: ResourceDeclaration) @Throws(IOException::class) - fun readWatches(): String + fun addWatch(watchable: Watchable) @Throws(IOException::class) - fun createResourceNetwork(resource: SNode) + fun removeWatch(watchable: Watchable) @Throws(IOException::class) - fun startResource(resource: SNode) + fun readWatches(): String } \ No newline at end of file diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/debugger/WatchablePath.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/debugger/WatchablePath.kt index 99988315b..0b41b376e 100644 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/debugger/WatchablePath.kt +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/debugger/WatchablePath.kt @@ -1,9 +1,9 @@ package org.fbme.ide.platform.debugger import org.fbme.lib.iec61499.declarations.ResourceDeclaration -import org.fbme.lib.iec61499.fbnetwork.FunctionBlockDeclaration +import org.fbme.lib.iec61499.fbnetwork.FunctionBlockDeclarationBase -data class WatchablePath(val root: ResourceDeclaration, val path: List) { +data class WatchablePath(val root: ResourceDeclaration, val path: List) { fun serialize(): WatchablePathData { return WatchablePathData(root.identifier, path.map { it.identifier }) diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/debugger/WatchedValueListener.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/debugger/WatchedValueListener.kt index 9510e3485..bcdf785d0 100644 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/debugger/WatchedValueListener.kt +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/debugger/WatchedValueListener.kt @@ -1,5 +1,5 @@ package org.fbme.ide.platform.debugger -interface WatchedValueListener { +fun interface WatchedValueListener { fun onValueChanged(newValue: String) } diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/debugger/WatcherFacade.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/debugger/WatcherFacade.kt index d3c42930b..522487495 100644 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/debugger/WatcherFacade.kt +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/debugger/WatcherFacade.kt @@ -1,6 +1,7 @@ package org.fbme.ide.platform.debugger import com.intellij.openapi.Disposable +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import jetbrains.mps.project.MPSProject @@ -23,7 +24,7 @@ class WatcherFacade(project: Project) : Disposable { private val watchedValueListeners: MutableMap> = HashMap() private val repository = PlatformRepositoryProvider.getInstance(project.getComponent(MPSProject::class.java)) - private val devicesFacade = project.service() + private val devicesFacade get() = ApplicationManager.getApplication().getService(DevicesFacade::class.java) private val thread: Thread = Thread { try { diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/editor/HeaderedNodeEditor.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/editor/HeaderedNodeEditor.kt index eed7659c5..a6b521962 100644 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/editor/HeaderedNodeEditor.kt +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/editor/HeaderedNodeEditor.kt @@ -23,7 +23,7 @@ import java.io.StringReader import javax.swing.JComponent import javax.swing.JPanel -class HeaderedNodeEditor(val baseNode: SNode, mpsProject: Project) : BaseNodeEditor(mpsProject) { +class HeaderedNodeEditor(val baseNode: SNode, mpsProject: Project) : BaseNodeEditor(mpsProject, baseNode.reference) { private val myVirtualFile: MPSNodeVirtualFile private val headerPanel: JPanel private val headerInfoPanel = ProjectionHeaderInfoPanel(this, mpsProject) @@ -32,8 +32,7 @@ class HeaderedNodeEditor(val baseNode: SNode, mpsProject: Project) : BaseNodeEdi var projectionComponent: JComponent? = null private set - val baseNodeElement = PlatformRepositoryProvider.getInstance(mpsProject) - .getAdapter(baseNode, PlatformElement::class.java) + val baseNodeElement = PlatformRepositoryProvider.getInstance(mpsProject).adapter(baseNode) init { myVirtualFile = NodeVirtualFileSystem.getInstance().getFileFor(mpsProject.repository, baseNode) diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/editor/ProjectionChooserPanel.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/editor/ProjectionChooserPanel.kt index f4b0a6f59..ee544e941 100644 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/editor/ProjectionChooserPanel.kt +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/editor/ProjectionChooserPanel.kt @@ -16,7 +16,7 @@ import java.awt.FlowLayout class ProjectionChooserPanel(node: SNode, project: Project) : JBPanel(FlowLayout(FlowLayout.CENTER, 10, 5)) { - val element = PlatformRepositoryProvider.getInstance(project).getAdapter(node, PlatformElement::class.java) + val element = PlatformRepositoryProvider.getInstance(project).adapter(node) init { val controllers = EditorProjectionControllerProvider.EP_NAME.extensions diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/editor/SimpleEditorProjection.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/editor/SimpleEditorProjection.kt index b3a973ab7..1c5da49aa 100644 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/editor/SimpleEditorProjection.kt +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/editor/SimpleEditorProjection.kt @@ -29,6 +29,7 @@ open class SimpleEditorProjection( get() = ProjectionHeaderInfo(rootElement, subElements, controller.id) override fun saveState(element: Element) {} + override fun getData(@NonNls string: String): Any? { return null } diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/persistence/PersistenceReferencesFixer.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/persistence/PersistenceReferencesFixer.kt index a54c82d7d..f6f050177 100644 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/persistence/PersistenceReferencesFixer.kt +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/persistence/PersistenceReferencesFixer.kt @@ -66,7 +66,9 @@ class PersistenceReferencesFixer(ideaProject: Project) : Disposable { } override fun dispose() { - listener.unsubscribeFrom(project.repository) + project.modelAccess.runReadAction { + listener.unsubscribeFrom(project.repository) + } } class Starter : StartupActivity { diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/projectWizard/Iec61499ProjectTemplate.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/projectWizard/Iec61499ProjectTemplate.kt index ec48f2411..93974a45d 100644 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/projectWizard/Iec61499ProjectTemplate.kt +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/projectWizard/Iec61499ProjectTemplate.kt @@ -1,9 +1,8 @@ package org.fbme.ide.platform.projectWizard +import com.intellij.openapi.project.Project import com.intellij.openapi.startup.StartupManager -import fbme.platform.PlatformIcons import jetbrains.mps.ide.newSolutionDialog.NewModuleUtil -import jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations import jetbrains.mps.openapi.navigation.NavigationSupport import jetbrains.mps.persistence.DefaultModelRoot import jetbrains.mps.persistence.ModelCannotBeCreatedException @@ -57,19 +56,20 @@ abstract class Iec61499ProjectTemplate( throw RuntimeException("Model can not be created", e) } val repository = PlatformRepositoryProvider.getInstance(project) - val initialElement = initModel(repository, model) + val initialElement = initModel(project.project, repository, model) model.module.declaredDependencies val initialNode = initialElement.node project.repository.modelAccess.runReadInEDT { - NavigationSupport.getInstance().openNode(project, initialNode, true, false) - NavigationSupport.getInstance().selectInTree(project, initialNode, false) + val navigationSupport = NavigationSupport.getInstance() + navigationSupport.openNode(project, initialNode, true, false) + navigationSupport.selectInTree(project, initialNode, false) } } } } } - abstract fun initModel(repository: PlatformRepository, model: SModel): PlatformElement + abstract fun initModel(project: Project, repository: PlatformRepository, model: SModel): PlatformElement override fun getIcon() = icon diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/projectWizard/LibraryTemplate.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/projectWizard/LibraryTemplate.kt index 97c64c0ba..a2e85f3ff 100644 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/projectWizard/LibraryTemplate.kt +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/projectWizard/LibraryTemplate.kt @@ -1,5 +1,6 @@ package org.fbme.ide.platform.projectWizard +import com.intellij.openapi.project.Project import fbme.platform.PlatformIcons import org.fbme.ide.iec61499.repository.PlatformElement import org.fbme.ide.iec61499.repository.PlatformRepository @@ -13,7 +14,7 @@ class LibraryTemplate : Iec61499ProjectTemplate( "library" ) { - override fun initModel(repository: PlatformRepository, model: SModel): PlatformElement { + override fun initModel(project: Project, repository: PlatformRepository, model: SModel): PlatformElement { val basicFBType = repository.iec61499Factory.createBasicFBTypeDeclaration(null) basicFBType.name = "SampleBasicBFType" diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/projectWizard/SystemTemplate.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/projectWizard/SystemTemplate.kt index 330d2e260..c0f91e516 100644 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/projectWizard/SystemTemplate.kt +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/projectWizard/SystemTemplate.kt @@ -1,5 +1,6 @@ package org.fbme.ide.platform.projectWizard +import com.intellij.openapi.project.Project import fbme.platform.PlatformIcons import org.fbme.ide.iec61499.repository.PlatformElement import org.fbme.ide.iec61499.repository.PlatformRepository @@ -13,7 +14,7 @@ class SystemTemplate : Iec61499ProjectTemplate( "system" ) { - override fun initModel(repository: PlatformRepository, model: SModel): PlatformElement { + override fun initModel(project: Project, repository: PlatformRepository, model: SModel): PlatformElement { val system = repository.iec61499Factory.createSystemDeclaration(null) val application = repository.iec61499Factory.createApplicationDeclaration(null) diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/PlatformTestRunner.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/PlatformTestRunner.kt index d185942ae..1bf26489a 100644 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/PlatformTestRunner.kt +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/PlatformTestRunner.kt @@ -87,7 +87,7 @@ class PlatformTestRunner(clazz: Class<*>) : BlockJUnit4ClassRunner(loadFromCusto private fun loadFromModuleName(clazz: Class<*>): String { val loadFrom = clazz.getAnnotation(LoadFrom::class.java) - return loadFrom?.module ?: "org.fbme.ide.platform" + return loadFrom?.module ?: "org.fbme.ide.iec61499.adapter" } @Throws(MalformedURLException::class, ClassNotFoundException::class, NoSuchFieldException::class) diff --git a/code/richediting/solutions/org.fbme.ide.richediting/models/org.fbme.ide.richediting.plugin.mps b/code/richediting/solutions/org.fbme.ide.richediting/models/org.fbme.ide.richediting.plugin.mps index 4ce83690c..f9cc459f5 100644 --- a/code/richediting/solutions/org.fbme.ide.richediting/models/org.fbme.ide.richediting.plugin.mps +++ b/code/richediting/solutions/org.fbme.ide.richediting/models/org.fbme.ide.richediting.plugin.mps @@ -227,21 +227,12 @@ - - - - - - - - - @@ -259,9 +250,7 @@ - - @@ -274,13 +263,11 @@ - - @@ -310,7 +297,6 @@ - @@ -334,7 +320,6 @@ - @@ -714,681 +699,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/code/richediting/src/main/kotlin/org/fbme/ide/richediting/actions/AllAlgorithmBodyVisibilityAction.kt b/code/richediting/src/main/kotlin/org/fbme/ide/richediting/actions/AllAlgorithmBodyVisibilityAction.kt index 88a47abe2..0db3dc800 100644 --- a/code/richediting/src/main/kotlin/org/fbme/ide/richediting/actions/AllAlgorithmBodyVisibilityAction.kt +++ b/code/richediting/src/main/kotlin/org/fbme/ide/richediting/actions/AllAlgorithmBodyVisibilityAction.kt @@ -14,8 +14,13 @@ abstract class AllAlgorithmBodyVisibilityAction : AnAction(), DumbAware { abstract val open: Boolean override fun update(event: AnActionEvent) { - event.presentation.isEnabledAndVisible = - event.getData(MPSEditorDataKeys.EDITOR_CELL)?.scene != null && event.element() != null + if (event.getData(MPSEditorDataKeys.EDITOR_CELL)?.scene == null) { + event.presentation.isEnabledAndVisible = false + return + } + event.executeReadAction { + event.presentation.isEnabledAndVisible = event.element() != null + } } override fun actionPerformed(event: AnActionEvent) { diff --git a/code/richediting/src/main/kotlin/org/fbme/ide/richediting/actions/AllStateActionVisibilityAction.kt b/code/richediting/src/main/kotlin/org/fbme/ide/richediting/actions/AllStateActionVisibilityAction.kt index c1105e8a8..f66dba9b5 100644 --- a/code/richediting/src/main/kotlin/org/fbme/ide/richediting/actions/AllStateActionVisibilityAction.kt +++ b/code/richediting/src/main/kotlin/org/fbme/ide/richediting/actions/AllStateActionVisibilityAction.kt @@ -14,8 +14,13 @@ abstract class AllStateActionVisibilityAction : AnAction(), DumbAware { abstract val open: Boolean override fun update(event: AnActionEvent) { - event.presentation.isEnabledAndVisible = - event.getData(MPSEditorDataKeys.EDITOR_CELL)?.scene != null && event.element() != null + if (event.getData(MPSEditorDataKeys.EDITOR_CELL)?.scene == null) { + event.presentation.isEnabledAndVisible = false + return + } + event.executeReadAction { + event.presentation.isEnabledAndVisible = event.element() != null + } } override fun actionPerformed(event: AnActionEvent) { diff --git a/code/richediting/src/main/kotlin/org/fbme/ide/richediting/actions/EditorActions.kt b/code/richediting/src/main/kotlin/org/fbme/ide/richediting/actions/EditorActions.kt index 703246a1d..5a1cdb9d6 100644 --- a/code/richediting/src/main/kotlin/org/fbme/ide/richediting/actions/EditorActions.kt +++ b/code/richediting/src/main/kotlin/org/fbme/ide/richediting/actions/EditorActions.kt @@ -39,7 +39,9 @@ fun AnActionEvent.executeActionInEditor(body: () -> Unit) { editorComponent.updater.update() } +val AnActionEvent.cell get() = getData(MPSEditorDataKeys.EDITOR_CELL) val AnActionEvent.repository get() = PlatformRepositoryProvider.getInstance(getRequiredData(MPSCommonDataKeys.MPS_PROJECT)) +val AnActionEvent.mpsProject get() = getData(MPSCommonDataKeys.MPS_PROJECT) inline fun AnActionEvent.element(): T? { var node = getData(MPSCommonDataKeys.NODE) diff --git a/code/richediting/src/main/kotlin/org/fbme/ide/richediting/adapters/fbnetwork/FBNetworkEditors.kt b/code/richediting/src/main/kotlin/org/fbme/ide/richediting/adapters/fbnetwork/FBNetworkEditors.kt index 7152771ca..f1c24488d 100644 --- a/code/richediting/src/main/kotlin/org/fbme/ide/richediting/adapters/fbnetwork/FBNetworkEditors.kt +++ b/code/richediting/src/main/kotlin/org/fbme/ide/richediting/adapters/fbnetwork/FBNetworkEditors.kt @@ -60,9 +60,9 @@ object FBNetworkEditors { val repository: PlatformElementsOwner = PlatformRepositoryProvider.getInstance(context.operationContext.project) val node = extView.associatedNode.parent ?: error("Parameter assignment is null") - val parameter = repository.getAdapter(node, ParameterAssignment::class.java) + val parameter = repository.adapterOrNull(node) val cell = RicheditingMpsBridge.createInlineValueCell(context, node) - if (parameter.value == null) { + if (parameter?.value == null) { val action: CellAction = object : CellAction { override fun getDescriptionText(): String { return "Delete empty constant" @@ -77,7 +77,7 @@ object FBNetworkEditors { } override fun execute(context: EditorContext) { - parameter.remove() + parameter?.remove() } } cell.setAction(CellActionType.DELETE, action) diff --git a/code/richediting/src/main/kotlin/org/fbme/ide/richediting/editor/RichResourceProjection.kt b/code/richediting/src/main/kotlin/org/fbme/ide/richediting/editor/RichResourceProjection.kt index 557479e84..88d89d7e7 100644 --- a/code/richediting/src/main/kotlin/org/fbme/ide/richediting/editor/RichResourceProjection.kt +++ b/code/richediting/src/main/kotlin/org/fbme/ide/richediting/editor/RichResourceProjection.kt @@ -6,6 +6,7 @@ import org.fbme.ide.platform.editor.EditorProjectionController import org.fbme.ide.platform.editor.HeaderedNodeEditor import org.fbme.ide.platform.editor.NavigatableDeclaration import org.fbme.ide.platform.editor.SimpleEditorProjection +import org.fbme.lib.iec61499.declarations.DeviceDeclaration import org.fbme.lib.iec61499.declarations.ResourceDeclaration import org.fbme.lib.iec61499.instances.NetworkInstance import org.jdom.Element @@ -21,16 +22,20 @@ class RichResourceProjection( val instance: NetworkInstance, project: Project ) : SimpleEditorProjection(node, controller, project, name, hints) { + + private val resource = instance.rootInstance.declaration as ResourceDeclaration + private val device = resource.container as DeviceDeclaration + override fun getData(@NonNls string: String): Any? = when (string) { - RichEditorDataKeys.RESOURCE.name -> instance.rootInstance.declaration - RichEditorDataKeys.DEVICE.name -> (instance.rootInstance.declaration as ResourceDeclaration).container + RichEditorDataKeys.RESOURCE.name -> resource + RichEditorDataKeys.DEVICE.name -> device RichEditorDataKeys.NETWORK_INSTANCE.name -> instance else -> null } override val subElements: List get() = NetworkInstanceNavigationSupport.navigatablePath(project, instance).apply { - addFirst(NavigatableDeclaration.build(project, node, "Overview", (instance.rootInstance.declaration as ResourceDeclaration).container.name)) + addFirst(NavigatableDeclaration.build(project, node, "Overview", device.name)) } override val rootElement: NavigatableDeclaration get() = NavigatableDeclaration.build(project, node, "Overview", "Overview") diff --git a/code/richediting/src/main/kotlin/org/fbme/ide/richediting/editor/RichResourceProjectionController.kt b/code/richediting/src/main/kotlin/org/fbme/ide/richediting/editor/RichResourceProjectionController.kt index bc434a8d9..a6ebd1277 100644 --- a/code/richediting/src/main/kotlin/org/fbme/ide/richediting/editor/RichResourceProjectionController.kt +++ b/code/richediting/src/main/kotlin/org/fbme/ide/richediting/editor/RichResourceProjectionController.kt @@ -20,7 +20,7 @@ class RichResourceProjectionController( ) : EditorProjectionController { private val system: SystemDeclaration = PlatformRepositoryProvider .getInstance(project) - .getAdapter(node, SystemDeclaration::class.java) + .adapterOrNull(node) ?: error("System declaration is null") override val id: String get() = "Resource" diff --git a/code/richediting/src/main/kotlin/org/fbme/ide/richediting/plugin/InspectionsManagerRegistrar.kt b/code/richediting/src/main/kotlin/org/fbme/ide/richediting/plugin/InspectionsManagerRegistrar.kt new file mode 100644 index 000000000..fab8d885b --- /dev/null +++ b/code/richediting/src/main/kotlin/org/fbme/ide/richediting/plugin/InspectionsManagerRegistrar.kt @@ -0,0 +1,18 @@ +package org.fbme.ide.richediting.plugin + +import com.intellij.openapi.project.Project +import com.intellij.openapi.startup.StartupActivity +import com.intellij.openapi.util.Disposer +import jetbrains.mps.openapi.editor.extensions.EditorExtensionRegistry +import org.fbme.ide.richediting.inspections.InspectionManagerImpl + +class InspectionsManagerRegistrar: StartupActivity { + + override fun runActivity(project: Project) { + val extensionRegistry = project.getComponent(EditorExtensionRegistry::class.java) + extensionRegistry.registerExtension(InspectionManagerImpl.EDITOR_EXTENSION) + Disposer.register(project) { + extensionRegistry.unregisterExtension(InspectionManagerImpl.EDITOR_EXTENSION) + } + } +} \ No newline at end of file diff --git a/code/richediting/src/main/kotlin/org/fbme/ide/richediting/plugin/NetworkObservationKeys.kt b/code/richediting/src/main/kotlin/org/fbme/ide/richediting/plugin/NetworkObservationKeys.kt new file mode 100644 index 000000000..e8cbe735c --- /dev/null +++ b/code/richediting/src/main/kotlin/org/fbme/ide/richediting/plugin/NetworkObservationKeys.kt @@ -0,0 +1,25 @@ +package org.fbme.ide.richediting.plugin + +import org.fbme.lib.iec61499.fbnetwork.FunctionBlockDeclarationBase +import org.fbme.lib.iec61499.fbnetwork.PortPath +import org.fbme.lib.iec61499.instances.NetworkInstance + +object NetworkObservationKeys { + fun inspector(instance: NetworkInstance): Any { + return InspectorKey(instance) + } + + fun portListener(instance: NetworkInstance, port: PortPath<*>): Any { + return PortListenerKey(instance, port) + } + + fun eccStateListener(instance: NetworkInstance, fb: FunctionBlockDeclarationBase): Any { + return ECCStateListenerKey(instance, fb) + } + + private data class InspectorKey(val instance: NetworkInstance) + + private data class PortListenerKey(private val instance: NetworkInstance, private val port: PortPath<*>) + + private data class ECCStateListenerKey(val instance: NetworkInstance, val fb: FunctionBlockDeclarationBase) +} diff --git a/code/richediting/src/main/resources/META-INF/plugin.xml b/code/richediting/src/main/resources/META-INF/plugin.xml index 8e2e1e43a..561b0538d 100644 --- a/code/richediting/src/main/resources/META-INF/plugin.xml +++ b/code/richediting/src/main/resources/META-INF/plugin.xml @@ -11,6 +11,7 @@ + diff --git a/code/scenes/build.gradle.kts b/code/scenes/build.gradle.kts index be1a97033..9630d0840 100644 --- a/code/scenes/build.gradle.kts +++ b/code/scenes/build.gradle.kts @@ -6,7 +6,6 @@ plugins { } mps { - buildScriptName.set("fbme_scenes") moduleName.set("org.fbme.scenes.lib") } diff --git a/code/smv-debugger/build.gradle.kts b/code/smv-debugger/build.gradle.kts index 9a9a24015..341c084e1 100644 --- a/code/smv-debugger/build.gradle.kts +++ b/code/smv-debugger/build.gradle.kts @@ -11,6 +11,8 @@ dependencies { compileOnly(project(":code:platform")) compileOnly(project(":code:richediting")) + testImplementation(mpsDistribution()) + mpsImplementation(project(":code:library", "mps")) mpsImplementation(project(":code:language", "mps")) } diff --git a/docs/css/video_embedding.css b/docs/css/video_embedding.css new file mode 100644 index 000000000..b80e41305 --- /dev/null +++ b/docs/css/video_embedding.css @@ -0,0 +1,15 @@ +.video-container { + position: relative; + padding-bottom: 56.25%; + padding-top: 35px; + height: 0; + overflow: hidden; +} + +.video-container iframe { + position: absolute; + top:0; + left: 0; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 0b92d2f7b..a3641060a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -14,3 +14,8 @@ toc: false * Launch your systems via _4diac FORTE_ runtime. * Monitor launched applications and gather their execution logs. * Test and analyze your applications by running them on the simulator. + +
+ +
+ diff --git a/samples/sandbox/solutions/org.fbme.ide.sandbox/models/org.fbme.ide.iec61499.lang.sandbox.blinky.mps b/samples/sandbox/solutions/org.fbme.ide.sandbox/models/org.fbme.ide.iec61499.lang.sandbox.blinky.mps index 77ad4dbfb..6beb1b7fa 100644 --- a/samples/sandbox/solutions/org.fbme.ide.sandbox/models/org.fbme.ide.iec61499.lang.sandbox.blinky.mps +++ b/samples/sandbox/solutions/org.fbme.ide.sandbox/models/org.fbme.ide.iec61499.lang.sandbox.blinky.mps @@ -181,60 +181,62 @@ - + - + + + + - - + - + - + - + - + - + - + - + - - + + - + - + - - + + - + @@ -242,7 +244,7 @@ - + @@ -250,10 +252,10 @@ - + - - + + @@ -263,10 +265,10 @@ - + - - + + @@ -276,9 +278,11 @@ - + + + + - @@ -293,11 +297,11 @@ - + - + @@ -308,11 +312,11 @@ - + - + @@ -323,36 +327,42 @@ - + - + - + - + - + + + + + + + - + @@ -360,7 +370,7 @@ - + @@ -369,11 +379,11 @@ - + - + diff --git a/settings.gradle.kts b/settings.gradle.kts index 14f191ebb..979cf2ffc 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,20 +1,21 @@ rootProject.name = "FBME" include( - "code:4diac-integration", - "code:enas", - "code:language", - "code:library", - "code:nxt-integration", - "code:platform", - "code:richediting", - "code:scenes", - "code:smv-debugger", - "code:debugger", +"code:4diac-integration", +"code:enas", +"code:language", +"code:library", +"code:nxt-integration", +"code:platform", +"code:richediting", +"code:scenes", +"code:smv-debugger", +"code:debugger", - "docs", +"docs", - "samples:statistics-plugin", - "samples:sandbox", - "samples:smv-debugger" +"samples:statistics-plugin", +"samples:sandbox", +"samples:smv-debugger", +"cat_visual" ) \ No newline at end of file From ab87773a4ee05a3df8b86a0bbf6ffb4fd0039b3d Mon Sep 17 00:00:00 2001 From: Pechenka2005 Date: Fri, 6 Oct 2023 21:56:15 +0300 Subject: [PATCH 17/17] create canvas element architcture revert change by mistake Fix build-bootstrap.xml Simplify configuration of platform tests. No referencing of mps-generated is now allowed in tests. rebuild: update build-bootstrap.xml --- .idea/compiler.xml | 8 + .idea/kotlinc.xml | 2 +- code/cat_visual/build.gradle.kts | 11 +- .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 63721 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + code/cat_visual/src/main/kotlin/Main.kt | 120 ++- .../kotlin/canvas/exceptions/Exceptions.kt | 4 + .../src/main/kotlin/canvas/items/Draggable.kt | 34 + .../kotlin/canvas/items/impl/AbstractItem.kt | 47 + .../canvas/items/{ => impl}/CustomItem.kt | 11 +- .../main/kotlin/canvas/items/impl/LampItem.kt | 72 ++ .../main/kotlin/canvas/items/impl/TextItem.kt | 141 +++ .../items/interfaces/CanvasItemInterface.kt | 28 + .../canvas/items/interfaces/Draggable.kt | 35 + .../canvas/items/interfaces/Focusable.kt | 24 + .../kotlin/canvas/items/model/Connection.kt | 28 + .../canvas/items/model/JsonCanvasModel.kt | 38 + .../canvas/items/model/figures/Templates.kt | 21 + .../src/main/kotlin/config/catBlocks/Lamp.cfg | 9 + .../src/main/kotlin/config/catBlocks/Text.cfg | 8 + .../src/main/kotlin/config/example.cfg | 12 + .../src/main/kotlin/config/example.sys | 17 + .../src/main/kotlin/config/hmi/Lamp.cfg | 12 + .../src/main/kotlin/config/hmi/Text.cfg | 8 + .../src/main/kotlin/connection/clients.kt | 8 +- .../main/kotlin/example/COUNTER/COUNTER.kt | 4 +- .../main/kotlin/example/COUNTER/COUNTER.xml | 16 +- .../src/main/kotlin/example/INPUTS/inp.json | 28 + .../kotlin/serializer/SerializationUtils.kt | 36 + .../main/kotlin/serializer/saved/test.json | 20 + .../src/main/kotlin/xmlParser/XmlParser.kt | 110 +++ .../debugger/simulator/ui/SimulatorPanel.kt | 16 +- .../models/org.fbme.language.build.mps | 5 + .../org.fbme.ide.iec61499.lang.editor.mps | 9 +- .../org.fbme.ide.iec61499.lang.intentions.mps | 2 +- .../org.fbme.ide.iec61499.lang.structure.mps | 18 + .../models/org.fbme.ide.st.lang.editor.mps | 128 +-- .../models/org.fbme.ide.st.lang.structure.mps | 74 +- .../org.fbme.ide.iec61499.adapter.st.mps | 918 ++++++++++++++++++ .../org.fbme.ide.iec61499.repository.mps | 335 +++++++ .../repository/PlatformDeclarationsScope.kt | 2 +- .../main/antlr/org/fbme/lib/st/parser/ST.g4 | 13 +- .../kotlin/org/fbme/lib/common/Identifier.kt | 2 +- .../org/fbme/lib/common/StringIdentifier.kt | 13 +- .../parser/ParameterDeclarationConverter.kt | 14 +- .../fbme/lib/iec61499/parser/STConverter.kt | 27 + .../DependentDeclarationGenerator.kt | 15 + .../FBInterfacePrinterWithAdapters.kt | 5 +- .../stringify/HMIInterfaceTypeGenerator.kt | 64 +- .../stringify/ParameterDeclarationPrinter.kt | 9 +- .../main/kotlin/org/fbme/lib/st/STFactory.kt | 7 +- .../kotlin/org/fbme/lib/st/types/ArrayType.kt | 10 + .../fbme/lib/st/types/ArrayTypeDimensions.kt | 8 + .../org/fbme/lib/st/types/ArrayTypeSizes.kt | 7 + .../fbme/lib/st/types/ArrayTypeSubranges.kt | 7 + .../org/fbme/lib/st/types/DerivedType.kt | 2 +- .../org/fbme/lib/st/types/ElementaryType.kt | 2 +- .../org/fbme/lib/st/types/NonGenericType.kt | 3 + .../main/kotlin/org/fbme/lib/st/types/Size.kt | 7 + .../kotlin/org/fbme/lib/st/types/Subrange.kt | 8 + .../lib/nxt-integration.jar | Bin 0 -> 60137 bytes .../importer/BasicFbTypeNxtImporterTest.kt | 3 - .../platform/testing/IdeaTestEnvironment.kt | 43 + .../org/fbme/ide/platform/testing/LoadFrom.kt | 5 - .../ide/platform/testing/PlatformTestBase.kt | 8 +- .../platform/testing/PlatformTestRunner.kt | 148 --- .../adapters/FBNetworkConverterTest.kt | 3 - .../ide/platform/adapters/FBResolveTest.kt | 2 - .../platform/adapters/IEC61499FactoryTest.kt | 3 - .../models/org.fbme.ide.eccSandbox/null.fbt | 8 + .../TestArrayVariables.fbt | 40 + .../lib/statistics-plugin.jar | Bin 0 -> 4088 bytes 72 files changed, 2507 insertions(+), 405 deletions(-) create mode 100644 code/cat_visual/gradle/wrapper/gradle-wrapper.jar create mode 100644 code/cat_visual/gradle/wrapper/gradle-wrapper.properties create mode 100644 code/cat_visual/src/main/kotlin/canvas/exceptions/Exceptions.kt create mode 100644 code/cat_visual/src/main/kotlin/canvas/items/Draggable.kt create mode 100644 code/cat_visual/src/main/kotlin/canvas/items/impl/AbstractItem.kt rename code/cat_visual/src/main/kotlin/canvas/items/{ => impl}/CustomItem.kt (91%) create mode 100644 code/cat_visual/src/main/kotlin/canvas/items/impl/LampItem.kt create mode 100644 code/cat_visual/src/main/kotlin/canvas/items/impl/TextItem.kt create mode 100644 code/cat_visual/src/main/kotlin/canvas/items/interfaces/CanvasItemInterface.kt create mode 100644 code/cat_visual/src/main/kotlin/canvas/items/interfaces/Draggable.kt create mode 100644 code/cat_visual/src/main/kotlin/canvas/items/interfaces/Focusable.kt create mode 100644 code/cat_visual/src/main/kotlin/canvas/items/model/Connection.kt create mode 100644 code/cat_visual/src/main/kotlin/canvas/items/model/JsonCanvasModel.kt create mode 100644 code/cat_visual/src/main/kotlin/canvas/items/model/figures/Templates.kt create mode 100644 code/cat_visual/src/main/kotlin/config/catBlocks/Lamp.cfg create mode 100644 code/cat_visual/src/main/kotlin/config/catBlocks/Text.cfg create mode 100644 code/cat_visual/src/main/kotlin/config/example.cfg create mode 100644 code/cat_visual/src/main/kotlin/config/example.sys create mode 100644 code/cat_visual/src/main/kotlin/config/hmi/Lamp.cfg create mode 100644 code/cat_visual/src/main/kotlin/config/hmi/Text.cfg create mode 100644 code/cat_visual/src/main/kotlin/example/INPUTS/inp.json create mode 100644 code/cat_visual/src/main/kotlin/serializer/SerializationUtils.kt create mode 100644 code/cat_visual/src/main/kotlin/serializer/saved/test.json create mode 100644 code/cat_visual/src/main/kotlin/xmlParser/XmlParser.kt create mode 100644 code/library/src/main/kotlin/org/fbme/lib/st/types/ArrayType.kt create mode 100644 code/library/src/main/kotlin/org/fbme/lib/st/types/ArrayTypeDimensions.kt create mode 100644 code/library/src/main/kotlin/org/fbme/lib/st/types/ArrayTypeSizes.kt create mode 100644 code/library/src/main/kotlin/org/fbme/lib/st/types/ArrayTypeSubranges.kt create mode 100644 code/library/src/main/kotlin/org/fbme/lib/st/types/NonGenericType.kt create mode 100644 code/library/src/main/kotlin/org/fbme/lib/st/types/Size.kt create mode 100644 code/library/src/main/kotlin/org/fbme/lib/st/types/Subrange.kt create mode 100644 code/nxt-integration/solutions/org.fbme.integration.nxt/lib/nxt-integration.jar create mode 100644 code/platform/src/main/kotlin/org/fbme/ide/platform/testing/IdeaTestEnvironment.kt delete mode 100644 code/platform/src/main/kotlin/org/fbme/ide/platform/testing/LoadFrom.kt delete mode 100644 code/platform/src/main/kotlin/org/fbme/ide/platform/testing/PlatformTestRunner.kt create mode 100644 samples/sandbox/solutions/org.fbme.ide.eccSandbox/models/org.fbme.ide.eccSandbox/null.fbt create mode 100644 samples/sandbox/solutions/org.fbme.ide.sandbox/models/org.fbme.iec61499.lang.sandbox.standard/TestArrayVariables.fbt create mode 100644 samples/statistics-plugin/solutions/org.fbme.samples.statisticsPlugin/lib/statistics-plugin.jar diff --git a/.idea/compiler.xml b/.idea/compiler.xml index c8952aa4d..58d13c02c 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,14 @@ + + + + + + + + diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 7e340a776..9a55c2de1 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/code/cat_visual/build.gradle.kts b/code/cat_visual/build.gradle.kts index 7586c8f9e..c92353dac 100644 --- a/code/cat_visual/build.gradle.kts +++ b/code/cat_visual/build.gradle.kts @@ -1,9 +1,8 @@ -import org.jetbrains.compose.compose import org.jetbrains.compose.desktop.application.dsl.TargetFormat import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - kotlin + kotlin("jvm") version "1.8.0" id("org.jetbrains.compose") version "1.3.1" kotlin("plugin.serialization") version "1.8.0" } @@ -38,12 +37,4 @@ compose.desktop { packageVersion = "1.0.0" } } -} -val compileKotlin: KotlinCompile by tasks -compileKotlin.kotlinOptions { - jvmTarget = "1.8" -} -val compileTestKotlin: KotlinCompile by tasks -compileTestKotlin.kotlinOptions { - jvmTarget = "1.8" } \ No newline at end of file diff --git a/code/cat_visual/gradle/wrapper/gradle-wrapper.jar b/code/cat_visual/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..7f93135c49b765f8051ef9d0a6055ff8e46073d8 GIT binary patch literal 63721 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鯊%1Et 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⁢ zv5o9#E7!G0XmdzfsS{efPNi`1b44~SZ4Z8fuX!I}#8g+(wxzQwUT#Xb2(tbY1+EUhGKoT@KEU9Ktl>_0 z%bjDJg;#*gtJZv!-Zs`?^}v5eKmnbjqlvnSzE@_SP|LG_PJ6CYU+6zY6>92%E+ z=j@TZf-iW4(%U{lnYxQA;7Q!b;^brF8n0D>)`q5>|WDDXLrqYU_tKN2>=#@~OE7grMnNh?UOz-O~6 z6%rHy{#h9K0AT+lDC7q4{hw^|q6*Ry;;L%Q@)Ga}$60_q%D)rv(CtS$CQbpq9|y1e zRSrN4;$Jyl{m5bZw`$8TGvb}(LpY{-cQ)fcyJv7l3S52TLXVDsphtv&aPuDk1OzCA z4A^QtC(!11`IsNx_HnSy?>EKpHJWT^wmS~hc^p^zIIh@9f6U@I2 zC=Mve{j2^)mS#U$e{@Q?SO6%LDsXz@SY+=cK_QMmXBIU)j!$ajc-zLx3V60EXJ!qC zi<%2x8Q24YN+&8U@CIlN zrZkcT9yh%LrlGS9`G)KdP(@9Eo-AQz@8GEFWcb7U=a0H^ZVbLmz{+&M7W(nXJ4sN8 zJLR7eeK(K8`2-}j(T7JsO`L!+CvbueT%izanm-^A1Dn{`1Nw`9P?cq;7no+XfC`K(GO9?O^5zNIt4M+M8LM0=7Gz8UA@Z0N+lg+cX)NfazRu z5D)~HA^(u%w^cz+@2@_#S|u>GpB+j4KzQ^&Wcl9f z&hG#bCA(Yk0D&t&aJE^xME^&E-&xGHhXn%}psEIj641H+Nl-}boj;)Zt*t(4wZ5DN z@GXF$bL=&pBq-#vkTkh>7hl%K5|3 z{`Vn9b$iR-SoGENp}bn4;fR3>9sA%X2@1L3aE9yTra;Wb#_`xWwLSLdfu+PAu+o3| zGVnpzPr=ch{uuoHjtw7+_!L_2;knQ!DuDl0R`|%jr+}jFzXtrHIKc323?JO{l&;VF z*L1+}JU7%QJOg|5|Tc|D8fN zJORAg=_vsy{ak|o);@)Yh8Lkcg@$FG3k@ep36BRa^>~UmnRPziS>Z=`Jb2x*Q#`%A zU*i3&Vg?TluO@X0O;r2Jl6LKLUOVhSqg1*qOt^|8*c7 zo(298@+r$k_wQNGHv{|$tW(T8L+4_`FQ{kEW5Jgg{yf7ey4ss_(SNKfz(N9lx&a;< je(UuV8hP?p&}TPdm1I$XmG#(RzlD&B2izSj9sl%y5~4qc literal 0 HcmV?d00001 diff --git a/code/cat_visual/gradle/wrapper/gradle-wrapper.properties b/code/cat_visual/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..3fa8f862f --- /dev/null +++ b/code/cat_visual/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/code/cat_visual/src/main/kotlin/Main.kt b/code/cat_visual/src/main/kotlin/Main.kt index f5710a74d..942d62ec4 100644 --- a/code/cat_visual/src/main/kotlin/Main.kt +++ b/code/cat_visual/src/main/kotlin/Main.kt @@ -1,35 +1,40 @@ import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.gestures.rememberTransformableState +import androidx.compose.foundation.gestures.transformable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.Button import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold -import androidx.compose.runtime.Composable +import androidx.compose.material.Text +import androidx.compose.runtime.* import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState -import canvas.items.CustomItem +import androidx.compose.ui.zIndex +import canvas.items.impl.CustomItem +import canvas.items.impl.LampItem +import canvas.items.impl.TextItem +import canvas.items.interfaces.CanvasItemInterface +import canvas.items.model.JsonCanvasModel +import canvas.items.model.toItem import connection.* -import connection.field.BoolField -import connection.field.TYPE_ID import example.COUNTER.CounterLampHMI -import example.WATER_TANK.System -import example.WATER_TANK.WaterTank -import serializer.PlainMapping +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import serializer.SerializationUtils import serializer.getConf import serializer.getMapping import serializer.getPlainMapping -import java.io.BufferedReader import java.io.File -import java.io.InputStreamReader -import java.net.DatagramPacket -import java.net.InetAddress -import java.net.ServerSocket @Composable fun CanvasContext(listFigures: SnapshotStateList) { @@ -64,6 +69,64 @@ val client = buildMappingClient("src/main/kotlin/example/COUNTER/COUNTER.xml", " //val client = buildMappingClient("src/main/kotlin/example/WATER_TANK/WATER_TANK.xml", "src/main/kotlin/example/WATER_TANK/WATER_TANK_CONF.xml", "") //val client = buildMappingClient("src/main/kotlin/example/WATER_TANK/WATER_TANK.xml", "src/main/kotlin/example/WATER_TANK/COMMON_CONF.xml", "json") +@Composable +fun canvas( + itemList: List, + deleteItem: (String) -> Unit, +) { + LazyColumn( + modifier = Modifier + .fillMaxHeight() + .fillMaxWidth() + .zIndex(0f), + ) { + items(itemList.size) { index -> + itemList[index].getContent { deleteItem(it)} + } + } +} + +@Composable +fun itemBar( + itemList: List, + addNewItem: (CanvasItemInterface) -> Unit +) { + Column( + modifier = Modifier + .background(Color.LightGray) + .width(100.dp) + .fillMaxHeight() + .zIndex(-2f), + horizontalAlignment = Alignment.End, + ) { + Button( + modifier = Modifier.align(alignment = Alignment.CenterHorizontally), + onClick = { + addNewItem(TextItem(content = "AddedText")) + } + ) { + Text(text = "Add text") + } + Button( + modifier = Modifier.align(alignment = Alignment.CenterHorizontally), + onClick = { + addNewItem(LampItem(client = client)) + } + ) { + Text(text = "Add lamp") + } + Button( + modifier = Modifier.align(alignment = Alignment.CenterHorizontally), + onClick = { + val x = itemList.map {it.toModel()} + val json = Json.encodeToString(x) + SerializationUtils.saveSnapshot(json) + } + ) { + Text(text = "save") + } + } +} fun main() = application { Window( @@ -71,9 +134,26 @@ fun main() = application { title = "Canvas for CAT", state = rememberWindowState(width = 600.dp, height = 600.dp) ) { - MaterialTheme { - CounterLampHMI(client, "1") - client.retrieveValues() - } + val listxxx = Json.decodeFromString>(SerializationUtils.readSnapshot()) + var itemList by remember { mutableStateOf(listxxx.map { it.toItem(client) }) } + MaterialTheme { + Row( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(), + ) { +// CounterLampHMI(client, "1") + itemBar(itemList) { item -> + itemList += listOf(item) + } + canvas(itemList) { idToDelete -> + itemList = itemList.filter { it.id.toString() != idToDelete } + val x = 1 + println(itemList.map { it.id }) + } + client.retrieveValues() + } + + } } } \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/canvas/exceptions/Exceptions.kt b/code/cat_visual/src/main/kotlin/canvas/exceptions/Exceptions.kt new file mode 100644 index 000000000..f76d22dd4 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/canvas/exceptions/Exceptions.kt @@ -0,0 +1,4 @@ +package canvas.exceptions + +class UnknownTypeItemException(id: String, unresolvedType: String): + RuntimeException("Can't resolve type: $unresolvedType, for id: $id") \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/canvas/items/Draggable.kt b/code/cat_visual/src/main/kotlin/canvas/items/Draggable.kt new file mode 100644 index 000000000..51125994a --- /dev/null +++ b/code/cat_visual/src/main/kotlin/canvas/items/Draggable.kt @@ -0,0 +1,34 @@ +package canvas.items + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.layout.offset +import androidx.compose.runtime.MutableState +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.changedToUp +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.dp + +interface Draggable { + fun Modifier.makeDraggable( + density: Density, + x: MutableState, + y: MutableState, + ): Modifier = + this.offset( + (x.value / density.density).dp, + (y.value / density.density).dp + ).pointerInput(Unit) { + detectDragGestures { change, dragAmount -> + change.consume() + x.value += dragAmount.x + y.value += dragAmount.y + } + + }.clickable { + this.background(Color.LightGray) + } +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/canvas/items/impl/AbstractItem.kt b/code/cat_visual/src/main/kotlin/canvas/items/impl/AbstractItem.kt new file mode 100644 index 000000000..6ef6d3271 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/canvas/items/impl/AbstractItem.kt @@ -0,0 +1,47 @@ +package canvas.items.impl + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import canvas.items.interfaces.CanvasItemInterface +import canvas.items.model.Input +import canvas.items.model.JsonCanvasModel +import canvas.items.model.Output +import connection.AbstractClient +import kotlinx.serialization.Serializable +import serializer.SerializationUtils +import java.util.UUID + +open class AbstractItem( + override val id: UUID = UUID.randomUUID(), + override var x: Float, + override var y: Float, + override val inputs: List = listOf(), + override val outputs: List = listOf(), + protected open val modifier: Modifier = Modifier, + protected open val content: Any? = null, + protected open val client: AbstractClient? = null, +): CanvasItemInterface { + + override val type: CanvasItemInterface.Type + get() = CanvasItemInterface.Type.UNKNOWN + + override fun toModel(): JsonCanvasModel = + JsonCanvasModel( + id = id.toString(), + x = x, + y = y, + inputs = inputs, + outputs = outputs, + type = type.toString(), + content = content.toString(), + ) + + @Composable + override fun getContent(deleteCurrentItem: (String) -> Unit) { + Box(modifier = modifier + .wrapContentSize()) + } +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/canvas/items/CustomItem.kt b/code/cat_visual/src/main/kotlin/canvas/items/impl/CustomItem.kt similarity index 91% rename from code/cat_visual/src/main/kotlin/canvas/items/CustomItem.kt rename to code/cat_visual/src/main/kotlin/canvas/items/impl/CustomItem.kt index 86447e4cf..298b8c5d1 100644 --- a/code/cat_visual/src/main/kotlin/canvas/items/CustomItem.kt +++ b/code/cat_visual/src/main/kotlin/canvas/items/impl/CustomItem.kt @@ -1,7 +1,5 @@ -package canvas.items +package canvas.items.impl -import androidx.compose.animation.animateContentSize -import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.gestures.* import androidx.compose.foundation.layout.* @@ -11,18 +9,11 @@ import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.input.pointer.PointerEventPass -import androidx.compose.ui.input.pointer.PointerEventType -import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.semantics.Role -import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp diff --git a/code/cat_visual/src/main/kotlin/canvas/items/impl/LampItem.kt b/code/cat_visual/src/main/kotlin/canvas/items/impl/LampItem.kt new file mode 100644 index 000000000..8b24df8df --- /dev/null +++ b/code/cat_visual/src/main/kotlin/canvas/items/impl/LampItem.kt @@ -0,0 +1,72 @@ +package canvas.items.impl + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.dp +import canvas.items.Draggable +import canvas.items.interfaces.CanvasItemInterface +import canvas.items.interfaces.Focusable +import canvas.items.model.Input +import canvas.items.model.Output +import canvas.items.model.figures.Round +import connection.AbstractClient +import connection.field.ConnectionField +import lib.elements.DARK_RADIAL_BRUSH +import lib.elements.LIGHT_RADIAL_BRUSH +import lib.elements.METALLIC_BRUSH +import java.util.* + +class LampItem( + override val id: UUID = UUID.randomUUID(), + override var x: Float = 0f, + override var y: Float = 0f, + override val modifier: Modifier = Modifier, + override val client: AbstractClient, +): AbstractItem( + x = x, + y = y, + modifier = modifier, + inputs = listOf(), + outputs = listOf(), + client = client, +), Draggable, Focusable { + + override val type = CanvasItemInterface.Type.LAMP + + @Composable + override fun getContent(deleteCurrentItem: (String) -> Unit) { + val isLighting = remember { mutableStateOf(false) } + val localDensity = LocalDensity.current + val xPosition = remember { mutableStateOf(x) } + val yPosition = remember { mutableStateOf(y) } + val focusRequester = remember { FocusRequester() } + val color = remember { mutableStateOf(Color.Black) } + Round( + modifier = modifier + .makeDraggable(localDensity, xPosition, yPosition) + .makeFocusable(focusRequester, color) + .background(brush = if (isLighting.value) LIGHT_RADIAL_BRUSH else DARK_RADIAL_BRUSH) + .border(width = 10.dp, brush = METALLIC_BRUSH, shape = CircleShape) + .clickable { + isLighting.value = if (isLighting.value) false else true + println("${xPosition.value} : ${yPosition.value}") + } + .wrapContentSize(), + height = 60.dp, + width = 60.dp, + ) + x = xPosition.value + y = yPosition.value + } +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/canvas/items/impl/TextItem.kt b/code/cat_visual/src/main/kotlin/canvas/items/impl/TextItem.kt new file mode 100644 index 000000000..f8904950f --- /dev/null +++ b/code/cat_visual/src/main/kotlin/canvas/items/impl/TextItem.kt @@ -0,0 +1,141 @@ +package canvas.items.impl + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import canvas.items.Draggable +import canvas.items.interfaces.CanvasItemInterface +import canvas.items.interfaces.Focusable +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import serializer.SerializationUtils +import java.util.* + +class TextItem( + override val id: UUID = UUID.randomUUID(), + override var x: Float = 0f, + override var y: Float = 0f, + override val modifier: Modifier = Modifier, + override var content: String, +): AbstractItem( + x = x, + y = y, + modifier = modifier, + content = content, +), Draggable, Focusable { + + override val type = CanvasItemInterface.Type.TEXT + + @Composable + override fun getContent(deleteCurrentItem: (String) -> Unit) { + val localDensity = LocalDensity.current + val idStaq by remember {mutableStateOf(id.toString())} + val xPosition = remember { mutableStateOf(x) } + val yPosition = remember { mutableStateOf(y) } + var currentText by remember { mutableStateOf(content) } + var currentTexInInputBar by remember { mutableStateOf("") } + var visibleInputBar by remember { mutableStateOf( false) } + val focusRequester = remember { FocusRequester() } + var expandedDropDownMenu by remember { mutableStateOf(false) } + val color = remember { mutableStateOf(Color.Black) } + Box( + modifier + .makeDraggable(localDensity, xPosition, yPosition) + ) { + Text( + text = currentText, + modifier = modifier + .makeFocusable(focusRequester, color) + .clickable { + println("${xPosition.value} : ${yPosition.value}") + println("$expandedDropDownMenu") + println(content) + println(currentText) + expandedDropDownMenu = !expandedDropDownMenu + } + .wrapContentSize(), + ) + DropdownMenu( + expanded = expandedDropDownMenu, + onDismissRequest = { + expandedDropDownMenu = false + }, + modifier = Modifier.width(200.dp) + ) { + DropdownMenuItem(onClick = { + deleteCurrentItem(idStaq) + }) { + Text( + text = "Удалить", + modifier = Modifier + .padding(5.dp), + fontSize = 15.sp, + ) + } + DropdownMenuItem( + onClick = { + visibleInputBar = !visibleInputBar + }, + ) { + Text( + text = "Изменить", + modifier = Modifier + .padding(5.dp), + fontSize = 15.sp, + ) + } + if (visibleInputBar) { + TextField( + value = currentTexInInputBar, + onValueChange = { currentTexInInputBar = it }, + modifier = Modifier + .fillMaxWidth(0.9f) + .align(Alignment.CenterHorizontally) + .padding(5.dp) + .height(50.dp) + ) + Row( + modifier = Modifier.width(200.dp) + ) { + Button( + modifier = Modifier + .width(80.dp).height(30.dp).offset(x = 10.dp), + onClick = { + currentText = currentTexInInputBar + currentTexInInputBar = "" + visibleInputBar = false + }, + + ) { + Text(text = "save", fontSize = 12.sp) + } + Button( + modifier = Modifier + .width(80.dp).height(30.dp).offset(x = 20.dp), + onClick = { + currentTexInInputBar = "" + visibleInputBar = false + } + ) { + Text(text = "cancel", fontSize = 12.sp) + } + } + } + + } + + } + x = xPosition.value + y = yPosition.value + content = currentText + } +} + diff --git a/code/cat_visual/src/main/kotlin/canvas/items/interfaces/CanvasItemInterface.kt b/code/cat_visual/src/main/kotlin/canvas/items/interfaces/CanvasItemInterface.kt new file mode 100644 index 000000000..ef767567b --- /dev/null +++ b/code/cat_visual/src/main/kotlin/canvas/items/interfaces/CanvasItemInterface.kt @@ -0,0 +1,28 @@ +package canvas.items.interfaces + +import androidx.compose.runtime.Composable +import canvas.items.model.Input +import canvas.items.model.JsonCanvasModel +import canvas.items.model.Output +import connection.AbstractClient +import java.util.UUID + +interface CanvasItemInterface { + val id: UUID + var x: Float + var y: Float + val inputs: List + val outputs: List + val type: Type + + @Composable + fun getContent(deleteCurrentItem: (String) -> Unit) + + fun toModel(): JsonCanvasModel + + enum class Type { + TEXT, + LAMP, + UNKNOWN, + } +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/canvas/items/interfaces/Draggable.kt b/code/cat_visual/src/main/kotlin/canvas/items/interfaces/Draggable.kt new file mode 100644 index 000000000..c03f20e81 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/canvas/items/interfaces/Draggable.kt @@ -0,0 +1,35 @@ +package canvas.items.interfaces + +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.focusable +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.layout.offset +import androidx.compose.runtime.MutableState +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.dp + +interface Draggable { + fun Modifier.makeDraggable( + localDensity: Density, + xPosition: MutableState, + yPosition: MutableState, + ): Modifier = + this.offset( + (xPosition.value / localDensity.density).dp, + (yPosition.value / localDensity.density).dp + ).pointerInput(Unit) { + detectDragGestures { change, dragAmount -> + change.consume() + xPosition.value += dragAmount.x + yPosition.value += dragAmount.y + } + + } +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/canvas/items/interfaces/Focusable.kt b/code/cat_visual/src/main/kotlin/canvas/items/interfaces/Focusable.kt new file mode 100644 index 000000000..c4e03c108 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/canvas/items/interfaces/Focusable.kt @@ -0,0 +1,24 @@ +package canvas.items.interfaces + +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.focusable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp + +interface Focusable { + fun Modifier.makeFocusable( + focusRequester: FocusRequester, + color: MutableState, + ): Modifier = + this.clickable { focusRequester.requestFocus() } + .border(1.dp, color.value) + .focusRequester(focusRequester) + .onFocusChanged { color.value = if (it.isFocused) Color.LightGray else Color.Transparent } + .focusable() +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/canvas/items/model/Connection.kt b/code/cat_visual/src/main/kotlin/canvas/items/model/Connection.kt new file mode 100644 index 000000000..1c0cfee2a --- /dev/null +++ b/code/cat_visual/src/main/kotlin/canvas/items/model/Connection.kt @@ -0,0 +1,28 @@ +package canvas.items.model + +import kotlinx.serialization.Serializable +import serializer.SerializationUtils +import java.util.UUID + +@Serializable +data class Input( + @Serializable(with = SerializationUtils.UUIDSerializer::class) + val id: UUID, + val type: TypeConnection, +) + +@Serializable +data class Output( + @Serializable(with = SerializationUtils.UUIDSerializer::class) + val id: UUID, + val type: TypeConnection, +) + +@Serializable +enum class TypeConnection { + BOOL, + STRING, + INT, + FLOAT +} + diff --git a/code/cat_visual/src/main/kotlin/canvas/items/model/JsonCanvasModel.kt b/code/cat_visual/src/main/kotlin/canvas/items/model/JsonCanvasModel.kt new file mode 100644 index 000000000..5153435dd --- /dev/null +++ b/code/cat_visual/src/main/kotlin/canvas/items/model/JsonCanvasModel.kt @@ -0,0 +1,38 @@ +package canvas.items.model + +import androidx.compose.ui.Modifier +import canvas.exceptions.UnknownTypeItemException +import canvas.items.impl.LampItem +import canvas.items.impl.TextItem +import canvas.items.interfaces.CanvasItemInterface +import connection.AbstractClient +import kotlinx.serialization.Serializable +import java.util.* + +@Serializable +data class JsonCanvasModel( + val id: String, + val x: Float, + val y: Float, + val inputs: List, + val outputs: List, + val content: String = "", + val type: String, +) + +fun JsonCanvasModel.toItem(client: AbstractClient): CanvasItemInterface = + when(type) { + CanvasItemInterface.Type.TEXT.name -> TextItem( + id = UUID.fromString(id), + x = x, + y = y, + content = content, + ) + CanvasItemInterface.Type.LAMP.name -> LampItem( + id = UUID.fromString(id), + x = x, + y = y, + client = client, + ) + else -> throw UnknownTypeItemException(id, type) + } \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/canvas/items/model/figures/Templates.kt b/code/cat_visual/src/main/kotlin/canvas/items/model/figures/Templates.kt new file mode 100644 index 000000000..23fcee40e --- /dev/null +++ b/code/cat_visual/src/main/kotlin/canvas/items/model/figures/Templates.kt @@ -0,0 +1,21 @@ +package canvas.items.model.figures + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.Dp + +@Composable +fun Round(modifier: Modifier, width: Dp, height: Dp) { + Box( + modifier = Modifier + .then(modifier) + .width(width) + .height(height) + .clip(CircleShape) + ) +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/config/catBlocks/Lamp.cfg b/code/cat_visual/src/main/kotlin/config/catBlocks/Lamp.cfg new file mode 100644 index 000000000..a029cb454 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/config/catBlocks/Lamp.cfg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/code/cat_visual/src/main/kotlin/config/catBlocks/Text.cfg b/code/cat_visual/src/main/kotlin/config/catBlocks/Text.cfg new file mode 100644 index 000000000..93168f5c1 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/config/catBlocks/Text.cfg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/code/cat_visual/src/main/kotlin/config/example.cfg b/code/cat_visual/src/main/kotlin/config/example.cfg new file mode 100644 index 000000000..5807dfa1e --- /dev/null +++ b/code/cat_visual/src/main/kotlin/config/example.cfg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/config/example.sys b/code/cat_visual/src/main/kotlin/config/example.sys new file mode 100644 index 000000000..885cdc0ed --- /dev/null +++ b/code/cat_visual/src/main/kotlin/config/example.sys @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/config/hmi/Lamp.cfg b/code/cat_visual/src/main/kotlin/config/hmi/Lamp.cfg new file mode 100644 index 000000000..ad818444a --- /dev/null +++ b/code/cat_visual/src/main/kotlin/config/hmi/Lamp.cfg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/code/cat_visual/src/main/kotlin/config/hmi/Text.cfg b/code/cat_visual/src/main/kotlin/config/hmi/Text.cfg new file mode 100644 index 000000000..344244ce0 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/config/hmi/Text.cfg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/code/cat_visual/src/main/kotlin/connection/clients.kt b/code/cat_visual/src/main/kotlin/connection/clients.kt index 99a76bee1..ec8c1dd40 100644 --- a/code/cat_visual/src/main/kotlin/connection/clients.kt +++ b/code/cat_visual/src/main/kotlin/connection/clients.kt @@ -33,16 +33,16 @@ abstract class UDPClient(): AbstractClient() { class PlainClient(mapping: PlainMapping): UDPClient() { init { - mapping.inputs.inputs.forEach({ + mapping.inputs.inputs.forEach { registry.getConnection(it.name, TYPE_ID.valueOf(it.type), it.host, it.port) inputs.add(it.name) inputConnections.add(registry.getConnection(it.name)!!.second) - }) - mapping.outputs.outputs.forEach({ + } + mapping.outputs.outputs.forEach { registry.getConnection(it.name, TYPE_ID.valueOf(it.type), it.host, it.port) outputs.add(it.name) outputConnections.add(registry.getConnection(it.name)!!.second) - }) + } } override fun sendValue(name: String) { diff --git a/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER.kt b/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER.kt index 309fe7173..3da411dcb 100644 --- a/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER.kt +++ b/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER.kt @@ -1,9 +1,11 @@ package example.COUNTER import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width +import androidx.compose.foundation.onClick import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -40,5 +42,5 @@ fun CounterHMI(client: AbstractClient, id: String) { fun CounterLampHMI(client: AbstractClient, id: String) { PositionedBox(children = {CounterHMI(client, "1")}, x = 200) LampHMI(client, "1") - PositionedBox(children = {example.COUNTER.ToggleHMI(client, "1")}, x = 300, y = 100) + PositionedBox(children = {ToggleHMI(client, "1")}, x = 300, y = 100) } \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER.xml b/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER.xml index 869c96923..d863c3198 100644 --- a/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER.xml +++ b/code/cat_visual/src/main/kotlin/example/COUNTER/COUNTER.xml @@ -7,4 +7,18 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/example/INPUTS/inp.json b/code/cat_visual/src/main/kotlin/example/INPUTS/inp.json new file mode 100644 index 000000000..6a69bf98c --- /dev/null +++ b/code/cat_visual/src/main/kotlin/example/INPUTS/inp.json @@ -0,0 +1,28 @@ +{ + "host": "", + "port": "", + "mapping": { + "id1": { + "inputs": [ + { + "id": "id", + "type": "type" + }, + { + "id": "id", + "type": "type" + } + ], + "outputs": [ + { + "id": "id", + "type": "type" + }, + { + "id": "id", + "type": "type" + } + ] + } + } +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/serializer/SerializationUtils.kt b/code/cat_visual/src/main/kotlin/serializer/SerializationUtils.kt new file mode 100644 index 000000000..5b48fbd58 --- /dev/null +++ b/code/cat_visual/src/main/kotlin/serializer/SerializationUtils.kt @@ -0,0 +1,36 @@ +package serializer + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import java.io.File +import java.util.* + +class SerializationUtils { + + object UUIDSerializer : KSerializer { + override val descriptor = PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): UUID { + return UUID.fromString(decoder.decodeString()) + } + + override fun serialize(encoder: Encoder, value: UUID) { + encoder.encodeString(value.toString()) + } + } + + companion object { + private const val PATH: String = "/Users/sj/IdeaProjects/fbme/code/cat_visual/src/main/kotlin/serializer/saved" + + fun saveSnapshot(json: String) { + File(PATH, "test.json").writeText(json) + } + + fun readSnapshot(): String { + return File(PATH, "test.json").readText() + } + } +} \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/serializer/saved/test.json b/code/cat_visual/src/main/kotlin/serializer/saved/test.json new file mode 100644 index 000000000..3cce12eae --- /dev/null +++ b/code/cat_visual/src/main/kotlin/serializer/saved/test.json @@ -0,0 +1,20 @@ +[ + { + "id": "1a83b4d3-98a2-47e2-b4f5-7a2c05ccc356", + "x": 604.0, + "y": 84.25, + "inputs": [], + "outputs": [], + "content": "null", + "type": "LAMP" + }, + { + "id": "997d8f1c-bfb6-4ade-8ac9-bdfb70a0e0cb", + "x": 0.0, + "y": 0.0, + "inputs": [], + "outputs": [], + "content": "qwerty", + "type": "TEXT" + } +] \ No newline at end of file diff --git a/code/cat_visual/src/main/kotlin/xmlParser/XmlParser.kt b/code/cat_visual/src/main/kotlin/xmlParser/XmlParser.kt new file mode 100644 index 000000000..55312ae3a --- /dev/null +++ b/code/cat_visual/src/main/kotlin/xmlParser/XmlParser.kt @@ -0,0 +1,110 @@ +package xmlParser + +import canvas.items.model.Input +import canvas.items.model.JsonCanvasModel +import canvas.items.model.Output +import org.w3c.dom.Document +import org.w3c.dom.Element +import java.io.File +import javax.xml.parsers.DocumentBuilderFactory + +class XmlParser( + private val path: String, +) { + + private val documentBuilder = DocumentBuilderFactory + .newInstance() + .newDocumentBuilder() + + fun parse(): List { + val parsedElements = mutableListOf() + val document = parseDocument(path) + val system = document.documentElement + val catTypes = system.childNodes + for (i in 0 until catTypes.length) { + val catType = catTypes.item(i) + if (catType.nodeType != Document.ELEMENT_NODE) { + continue + } + val catTypeNode = catType as Element + val nameAttribute = catTypeNode.getAttribute(AttributeName.NAME.value) + println(nameAttribute) + val instances = catTypeNode.childNodes + for (j in 0 until instances.length) { + val instance = instances.item(j) + if (instance.nodeType != Document.ELEMENT_NODE) { + continue + } + parsedElements.add( + toJsonCanvasModel(name = nameAttribute) + ) + val configName = getConfigName(nameAttribute) + parseSubBlock(parseDocument(configName), parsedElements) + } + } + return parsedElements + } + + + private fun getConfigName(catName: String): String { + return "$PATH_CAT_BLOCKS_CONFIG/$catName.cfg" + } + + private fun parseSubBlock( + document: Document, + parsedElements: MutableList + ) { + val system = document.documentElement + val catTypes = system.childNodes + for (i in 0 until catTypes.length) { + val catType = catTypes.item(i) + if (catType.nodeType != Document.ELEMENT_NODE) { + continue + } + val catTypeNode = catType as Element + val tagName = catTypeNode.tagName + if (tagName != "SubCAT") { + continue + } + val nameAttribute = catTypeNode.getAttribute(AttributeName.TYPE.value) + println(nameAttribute) + parsedElements.add(toJsonCanvasModel(name = nameAttribute)) + val configName = getConfigName(nameAttribute) + parseSubBlock(parseDocument(configName), parsedElements) + } + } + + private fun toJsonCanvasModel( + id: String = "e58ed763-928c-4155-bee9-fdbaaadc15f3", + name: String, + content: String = "text", + inputs: List = listOf(), + outputs: List = listOf(), + ): JsonCanvasModel = + JsonCanvasModel( + x = 0f, + y = 0f, + content = content, + type = name, + id = id, + inputs = inputs, + outputs = outputs, + ) + + private fun parseDocument(path: String): Document { + return documentBuilder.parse(File(path)) + } + + enum class AttributeName(val value: String) { + NAME("Name"), + NAMESPACE("Namespace"), + ID("ID"), + TYPE("Type") + } + + companion object { + private const val PATH_CONFIG = "src/main/kotlin/config" + private const val PATH_CAT_BLOCKS_CONFIG = "$PATH_CONFIG/catBlocks" + private const val PATH_HMI_CONFIG = "$PATH_CONFIG/hmi" + } +} \ No newline at end of file diff --git a/code/debugger/src/main/kotlin/org/fbme/debugger/simulator/ui/SimulatorPanel.kt b/code/debugger/src/main/kotlin/org/fbme/debugger/simulator/ui/SimulatorPanel.kt index 2c702cbe3..f7b66031e 100644 --- a/code/debugger/src/main/kotlin/org/fbme/debugger/simulator/ui/SimulatorPanel.kt +++ b/code/debugger/src/main/kotlin/org/fbme/debugger/simulator/ui/SimulatorPanel.kt @@ -43,17 +43,15 @@ class SimulatorPanel( ?.drop(1) ?.joinToString(".") ?.split(".") - if (statesList.selectedIndex == statesList.itemsCount - 1) { - val triggerItem = JBMenuItem("Trigger event", AllIcons.Actions.Execute) - triggerItem.addActionListener { - if (watchPath != null) { - val resolvedSimulator = simulator - .resolveSimulator(watchPath.dropLast(1))!! as FBSimulator - resolvedSimulator.triggerEvent(watchPath.last()) - } + val triggerItem = JBMenuItem("Trigger event", AllIcons.Actions.Execute) + triggerItem.addActionListener { + if (watchPath != null) { + val resolvedSimulator = simulator + .resolveSimulator(watchPath.dropLast(1))!! as FBSimulator + resolvedSimulator.triggerEvent(watchPath.last()) } - popup.add(triggerItem) } + popup.add(triggerItem) val explainItem = JBMenuItem("Why?", AllIcons.Debugger.Question_badge) explainItem.addActionListener { showExplanation(e) diff --git a/code/language/buildsolution/models/org.fbme.language.build.mps b/code/language/buildsolution/models/org.fbme.language.build.mps index 3b28370ae..b318f8383 100644 --- a/code/language/buildsolution/models/org.fbme.language.build.mps +++ b/code/language/buildsolution/models/org.fbme.language.build.mps @@ -417,6 +417,11 @@ + + + + + diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps index 537cf030b..543b3e73c 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.editor.mps @@ -846,15 +846,8 @@ - + - - - - - - - diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.intentions.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.intentions.mps index e6a4884b9..81075bdb0 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.intentions.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.intentions.mps @@ -204,7 +204,7 @@ - + diff --git a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps index ebc3e744a..21586be17 100644 --- a/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps +++ b/code/language/languages/org.fbme.ide.iec61499.lang/models/org.fbme.ide.iec61499.lang.structure.mps @@ -539,9 +539,27 @@ + + + + + + + + + + + + + + + + + + diff --git a/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.editor.mps b/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.editor.mps index 3e6ce0ea7..5a947bc38 100644 --- a/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.editor.mps +++ b/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.editor.mps @@ -40,9 +40,6 @@
- - - @@ -201,7 +198,6 @@
- @@ -316,7 +312,6 @@ - @@ -417,9 +412,6 @@ - - -
@@ -714,7 +706,7 @@ - + @@ -846,12 +838,8 @@ - + - - - - @@ -9716,7 +9704,7 @@ - + @@ -9725,12 +9713,12 @@ - + - + @@ -9886,111 +9874,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.structure.mps b/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.structure.mps index 36b4b0dc4..1cfdc9205 100644 --- a/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.structure.mps +++ b/code/language/languages/org.fbme.ide.st.lang/models/org.fbme.ide.st.lang.structure.mps @@ -822,7 +822,7 @@ - + @@ -870,7 +870,7 @@ - + @@ -965,7 +965,7 @@ - + @@ -978,7 +978,7 @@ - + @@ -1193,5 +1193,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.st.mps b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.st.mps index c7889f438..afc7f0200 100644 --- a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.st.mps +++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.adapter.st.mps @@ -19,6 +19,10 @@ + + + + @@ -80,6 +84,9 @@ + + + @@ -136,6 +143,7 @@ + @@ -4461,6 +4469,29 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -5175,6 +5206,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6417,5 +6486,854 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.repository.mps b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.repository.mps index 7b30eee98..b972c36e3 100644 --- a/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.repository.mps +++ b/code/language/solutions/org.fbme.ide.iec61499.adapter/models/org.fbme.ide.iec61499.repository.mps @@ -3844,6 +3844,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -5817,6 +5992,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformDeclarationsScope.kt b/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformDeclarationsScope.kt index ba62b7a53..6e04737b8 100644 --- a/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformDeclarationsScope.kt +++ b/code/language/src/main/kotlin/org/fbme/ide/iec61499/repository/PlatformDeclarationsScope.kt @@ -19,7 +19,7 @@ internal class PlatformDeclarationsScope( } override fun findCATBlockTypeDeclaration(identifier: Identifier): CATBlockTypeDeclaration? { - return findNode(identifier)?.let {myRepository.getAdapter(it, CATBlockTypeDeclaration::class.java)} + return findNode(identifier)?.let {repository.getAdapter(it, CATBlockTypeDeclaration::class.java)} } override fun findBasicFBTypeDeclaration(identifier: Identifier): BasicFBTypeDeclaration? { diff --git a/code/library/src/main/antlr/org/fbme/lib/st/parser/ST.g4 b/code/library/src/main/antlr/org/fbme/lib/st/parser/ST.g4 index 00b3ecfce..c973e0443 100644 --- a/code/library/src/main/antlr/org/fbme/lib/st/parser/ST.g4 +++ b/code/library/src/main/antlr/org/fbme/lib/st/parser/ST.g4 @@ -86,9 +86,18 @@ statementListWithVariableDeclarations ; variablesDeclaration - : name=ID ':' type=ID ';' - ; + : name=ID ':' type=ID ';' + ; + +subrange + : from=(SingedInteger|DecInteger) '..' to=(SingedInteger|DecInteger) + ; + +arrayTypeDimensions + : subranges+=subrange (',' subranges+=subrange)* #arrayTypeSubranges + | sizes+=DecInteger (',' sizes+=DecInteger)* #arrayTypeSizes + ; DecInteger: Dec ('_'|Dec)*; BinInteger: '2#' Bin ('_'|Bin)*; diff --git a/code/library/src/main/kotlin/org/fbme/lib/common/Identifier.kt b/code/library/src/main/kotlin/org/fbme/lib/common/Identifier.kt index 853e5ffdc..5a3d6a847 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/common/Identifier.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/common/Identifier.kt @@ -1,3 +1,3 @@ package org.fbme.lib.common -interface Identifier +interface Identifier diff --git a/code/library/src/main/kotlin/org/fbme/lib/common/StringIdentifier.kt b/code/library/src/main/kotlin/org/fbme/lib/common/StringIdentifier.kt index ee44ef8fe..6e814a3ee 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/common/StringIdentifier.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/common/StringIdentifier.kt @@ -1,14 +1,3 @@ package org.fbme.lib.common -class StringIdentifier(val value: String) : Identifier { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || javaClass != other.javaClass) return false - val that = other as StringIdentifier - return value == that.value - } - - override fun hashCode(): Int { - return value.hashCode() - } -} +data class StringIdentifier(val value: String) : Identifier diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/ParameterDeclarationConverter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/ParameterDeclarationConverter.kt index 1a32ca5d4..009648ab1 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/ParameterDeclarationConverter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/ParameterDeclarationConverter.kt @@ -2,13 +2,25 @@ package org.fbme.lib.iec61499.parser import org.fbme.lib.common.Identifier import org.fbme.lib.iec61499.declarations.ParameterDeclaration +import org.fbme.lib.st.types.NonGenericType class ParameterDeclarationConverter(arguments: ConverterArguments) : DeclarationConverterBase(arguments) { override fun extractDeclarationBody(identifier: Identifier?): ParameterDeclaration { checkNotNull(element) val parameter = factory.createParameterDeclaration(identifier) - parameter.type = STConverter.parseType(stFactory, element.getAttributeValue("Type")) + val type = STConverter.parseType(stFactory, element.getAttributeValue("Type")) + val arraySizeValue = element.getAttributeValue("ArraySize") + if (arraySizeValue != null) { + require(type is NonGenericType) + val arrayType = stFactory.createArrayType() + val dimensions = STConverter.parseArrayDimensions(stFactory, arraySizeValue) + arrayType.baseType = type + arrayType.dimensions = dimensions + parameter.type = arrayType + } else { + parameter.type = type + } val initialValueText = element.getAttributeValue("InitialValue") if (initialValueText != null) { parameter.initialValue = STConverter.parseLiteral(stFactory, initialValueText) diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/STConverter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/STConverter.kt index ba0eadf47..1155b8824 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/STConverter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/parser/STConverter.kt @@ -10,6 +10,7 @@ import org.fbme.lib.st.parser.STLexer import org.fbme.lib.st.parser.STParser import org.fbme.lib.st.parser.STParser.* import org.fbme.lib.st.statements.Statement +import org.fbme.lib.st.types.ArrayTypeDimensions import org.fbme.lib.st.types.DataType import org.fbme.lib.st.types.ElementaryType import org.fbme.lib.st.types.GenericType @@ -64,6 +65,32 @@ object STConverter { return factory.createDerivedType(StringIdentifier(text), text) } + fun parseArrayDimensions(factory: STFactory, text: String): ArrayTypeDimensions? { + val arrayDimensionsCtx = createParser(text).arrayTypeDimensions() + if (arrayDimensionsCtx is ArrayTypeSubrangesContext) { + val subranges = factory.createArrayTypeSubranges() + val subrangeCtxs = arrayDimensionsCtx.subranges + for (subrangeCtx in subrangeCtxs) { + val subrange = factory.createSubrange() + subrange.from = subrangeCtx.from.text.toInt() + subrange.to = subrangeCtx.to.text.toInt() + subranges.subranges += subrange + } + return subranges + } + if (arrayDimensionsCtx is ArrayTypeSizesContext) { + val sizes = factory.createArrayTypeSizes() + val sizeCtxs = arrayDimensionsCtx.sizes + for (sizeCtx in sizeCtxs) { + val size = factory.createSize() + size.value = sizeCtx.text.toInt() + sizes.sizes += size + } + return sizes + } + return null + } + private fun extractStatementList(factory: STFactory, statementListCtx: StatementListContext): List { val statements: MutableList = ArrayList() val statementCtxs = statementListCtx.statements diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/DependentDeclarationGenerator.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/DependentDeclarationGenerator.kt index a63070b9f..d66920d59 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/DependentDeclarationGenerator.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/DependentDeclarationGenerator.kt @@ -21,4 +21,19 @@ class DependentDeclarationGenerator(private val myDeclaration: Declaration, priv return rootElements } + fun getName(): String { + val name = when (myDeclaration) { + is HMIInterfaceTypeDeclaration -> HMIInterfaceTypeGenerator.getDeclarationName(myDeclaration.name) + else -> myDeclaration.name + } + return name + } + + fun getIdentifier(): String { + val name = when (myDeclaration) { + is HMIInterfaceTypeDeclaration -> HMIInterfaceTypeGenerator.getDeclarationName(myDeclaration.identifier.toString()) + else -> myDeclaration.name + } + return name + } } \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/FBInterfacePrinterWithAdapters.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/FBInterfacePrinterWithAdapters.kt index b86dc46d5..25aa76484 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/FBInterfacePrinterWithAdapters.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/FBInterfacePrinterWithAdapters.kt @@ -7,8 +7,7 @@ import org.jdom.Element class FBInterfacePrinterWithAdapters(declaration: FBInterfaceDeclarationWithAdapters) : PrinterBase(declaration) { - @JvmField - var myInterfacePrinter = FBInterfacePrinter(element, false) + val myInterfacePrinter = FBInterfacePrinter(element, false) fun print(): Element { val element = myInterfacePrinter.print() @@ -55,6 +54,8 @@ class FBInterfacePrinterWithAdapters(declaration: FBInterfaceDeclarationWithAdap DeclarationPrinterBase(plugDeclaration, "AdapterDeclaration") { override fun printDeclarationBody(element: Element) { element.setAttribute("Type", this.element.typeReference.presentation) + element.setAttribute("x", "" + this.element.x) + element.setAttribute("x", "" + this.element.y) } } diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt index 17ca8c5c2..6224a7243 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/HMIInterfaceTypeGenerator.kt @@ -9,6 +9,7 @@ import org.fbme.lib.iec61499.parser.Iec61499ConverterConfiguration import org.fbme.lib.iec61499.parser.STConverter import org.fbme.lib.st.STFactory import org.fbme.lib.st.expressions.Expression +import org.fbme.lib.st.expressions.Literal import org.fbme.lib.st.statements.Statement import org.fbme.lib.st.types.DataType import org.fbme.lib.st.types.ElementaryType @@ -21,8 +22,10 @@ class HMIInterfaceTypeGenerator(val declaration: HMIInterfaceTypeDeclaration, va fun generateDependents(): List { val elements = mutableListOf() + val name = getDeclarationName(declaration.name) + val identifier = declaration.identifier.toString() if (declaration.inputParameters.size > 0) { - val outFb = generateDispatchOut(factory, stFactory, declaration.inputParameters, declaration.name) + val outFb = generateDispatchOut(factory, stFactory, declaration.inputParameters, name, identifier) elements.add(outFb) } if (declaration.outputParameters.size > 0) { @@ -30,11 +33,12 @@ class HMIInterfaceTypeGenerator(val declaration: HMIInterfaceTypeDeclaration, va factory, stFactory, declaration.outputParameters, - declaration.name + name, + identifier ) elements.add(inFb) } - val compositeHMI = generateComposite(factory, stFactory, declaration) + val compositeHMI = generateComposite(factory, stFactory, declaration, identifier) elements.add(compositeHMI) return elements } @@ -49,7 +53,7 @@ companion object { ElementaryType.STRING ) - fun generateDispatchOut(factory: IEC61499Factory, stFactory: STFactory, outputVars: List, name: String = ""): FBTypeDeclaration { + fun generateDispatchOut(factory: IEC61499Factory, stFactory: STFactory, outputVars: List, name: String = "", identifier: String = ""): FBTypeDeclaration { val bfb = factory.createBasicFBTypeDeclaration(null) bfb.name = "DISPATCH_OUT_${name}" @@ -59,6 +63,7 @@ companion object { val nameDeclaration = generateParameterDeclaration(factory, "NAME", ElementaryType.STRING, listOf()) val mappingDeclaration = generateParameterDeclaration(factory, "MAPPING", ElementaryType.STRING, listOf()) + mappingDeclaration.initialValue = STConverter.parseLiteral(stFactory, identifier) bfb.inputParameters.add(mappingDeclaration) bfb.outputParameters.add(nameDeclaration) @@ -118,7 +123,7 @@ companion object { } - fun generateDispatchIn(factory: IEC61499Factory, stFactory: STFactory, inputVars: List, name: String = ""): FBTypeDeclaration { + fun generateDispatchIn(factory: IEC61499Factory, stFactory: STFactory, inputVars: List, name: String = "", identifier: String = ""): FBTypeDeclaration { val bfb = factory.createBasicFBTypeDeclaration(null) bfb.name = "DISPATCH_IN_${name}" val startState = factory.createStateDeclaration(null) @@ -130,6 +135,7 @@ companion object { val inConnections = generateTypedConnections(factory, listOf(reqEvent), CONNECTION_TYPES) val nameDeclaration = generateParameterDeclaration(factory, "NAME", ElementaryType.STRING, listOf(reqEvent)) val mappingDeclaration = generateParameterDeclaration(factory, "MAPPING", ElementaryType.STRING, listOf(reqEvent)) + mappingDeclaration.initialValue = STConverter.parseLiteral(stFactory, identifier) bfb.inputParameters.addAll(inConnections) bfb.inputParameters.add(nameDeclaration) bfb.inputParameters.add(mappingDeclaration) @@ -244,9 +250,26 @@ companion object { return STConverter.parseExpression(stFactory, code)!! } - fun generateComposite(factory: IEC61499Factory, stFactory: STFactory, declaration: HMIInterfaceTypeDeclaration): CompositeFBTypeDeclaration { + fun getDeclarationName(name: String): String { + if (name.length < 5) { + return name + } + if (name.endsWith("_HMI")) { + return name.take(name.length - 4) + } + if (name.length < 9) { + return name + } + if (name.endsWith("_HMI_CONF")) { + return name.take(name.length - 9) + } + return name + } + + fun generateComposite(factory: IEC61499Factory, stFactory: STFactory, declaration: HMIInterfaceTypeDeclaration, identifier: String = ""): CompositeFBTypeDeclaration { var cFB = factory.createCompositeFBTypeDeclaration(null) - cFB.name = declaration.name + "_HMI" + val targetBlockName = getDeclarationName(declaration.name) + cFB.name = targetBlockName + "_HMI" declaration.inputParameters.forEach { if (it.name != "MAPPING") { val pD = factory.createParameterDeclaration(null) @@ -270,6 +293,7 @@ companion object { val mappingInput = factory.createParameterDeclaration(null) mappingInput.name = "MAPPING" mappingInput.type = ElementaryType.STRING + mappingInput.initialValue = STConverter.parseLiteral(stFactory, identifier) cFB.inputParameters.add(mappingInput) cFB.inputEvents.add(initEvent) @@ -278,7 +302,7 @@ companion object { cFB.network.functionBlocks.add(generateCommunicationBlock("PUBLISH_1", factory, stFactory)) cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("JSON_SERIALIZER", factory)) - cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("DISPATCH_OUT_${declaration.name}", factory)) + cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("DISPATCH_OUT_$targetBlockName", factory)) // EVENTS GENERATION @@ -298,7 +322,7 @@ companion object { cFB.inputEvents.add(event) val cEDispatch = factory.createFBNetworkConnection(EntryKind.EVENT) cEDispatch.sourceReference.setFQName(event.name) - cEDispatch.targetReference.setFQName("DISPATCH_OUT_${declaration.name}.${event.name}") + cEDispatch.targetReference.setFQName("DISPATCH_OUT_$targetBlockName.${event.name}") cFB.network.eventConnections.add(cEDispatch) } @@ -313,12 +337,12 @@ companion object { .forEach{ val cDDispatch = factory.createFBNetworkConnection(EntryKind.DATA) cDDispatch.sourceReference.setFQName(it.name) - cDDispatch.targetReference.setFQName("DISPATCH_OUT_${declaration.name}.${it.name}") + cDDispatch.targetReference.setFQName("DISPATCH_OUT_$targetBlockName.${it.name}") cFB.network.dataConnections.add(cDDispatch) } val cDNameSerialize = factory.createFBNetworkConnection(EntryKind.DATA) - cDNameSerialize.sourceReference.setFQName("DISPATCH_OUT_${declaration.name}.NAME") + cDNameSerialize.sourceReference.setFQName("DISPATCH_OUT_$targetBlockName.NAME") cDNameSerialize.targetReference.setFQName("JSON_SERIALIZER.NAME") cFB.network.dataConnections.add(cDNameSerialize) @@ -326,11 +350,11 @@ companion object { HMIInterfaceTypeGenerator.CONNECTION_TYPES.forEach { val cEDispatch = factory.createFBNetworkConnection(EntryKind.EVENT) - cEDispatch.sourceReference.setFQName("DISPATCH_OUT_${declaration.name}.IS_${it.name}") + cEDispatch.sourceReference.setFQName("DISPATCH_OUT_$targetBlockName.IS_${it.name}") cEDispatch.targetReference.setFQName("JSON_SERIALIZER.IS_${it.name}") cFB.network.eventConnections.add(cEDispatch) val cDDispatch = factory.createFBNetworkConnection(EntryKind.DATA) - cDDispatch.sourceReference.setFQName("DISPATCH_OUT_${declaration.name}.${it.name}_VALUE") + cDDispatch.sourceReference.setFQName("DISPATCH_OUT_$targetBlockName.${it.name}_VALUE") cDDispatch.targetReference.setFQName("JSON_SERIALIZER.${it.name}_VALUE") cFB.network.dataConnections.add(cDDispatch) } @@ -342,7 +366,7 @@ companion object { cFB.network.functionBlocks.add(generateCommunicationBlock("SUBSCRIBE_1", factory, stFactory, 65012)) cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("JSON_DESERIALIZER", factory)) - cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("DISPATCH_IN_${declaration.name}", factory)) + cFB.network.functionBlocks.add(generateFunctionDeclarationNoParam("DISPATCH_IN_$targetBlockName", factory)) // EVENTS GENERATION @@ -358,7 +382,7 @@ companion object { val cEDeserializeDispatch = factory.createFBNetworkConnection(EntryKind.EVENT) cEDeserializeDispatch.sourceReference.setFQName("JSON_DESERIALIZER.RES") - cEDeserializeDispatch.targetReference.setFQName("DISPATCH_IN_${declaration.name}.REQ") + cEDeserializeDispatch.targetReference.setFQName("DISPATCH_IN_$targetBlockName.REQ") cFB.network.eventConnections.add(cEDeserializeDispatch) declaration.outputParameters.forEach { @@ -366,7 +390,7 @@ companion object { cFB.outputEvents.add(event) val cEDispatchRes = factory.createFBNetworkConnection(EntryKind.EVENT) - cEDispatchRes.sourceReference.setFQName("DISPATCH_IN_${declaration.name}.${event.name}") + cEDispatchRes.sourceReference.setFQName("DISPATCH_IN_$targetBlockName.${event.name}") cEDispatchRes.targetReference.setFQName(event.name) cFB.network.eventConnections.add(cEDispatchRes) } @@ -380,17 +404,17 @@ companion object { val cDDeserializeName = factory.createFBNetworkConnection(EntryKind.DATA) cDDeserializeName.sourceReference.setFQName("JSON_DESERIALIZER.NAME") - cDDeserializeName.targetReference.setFQName("DISPATCH_IN_${declaration.name}.NAME") + cDDeserializeName.targetReference.setFQName("DISPATCH_IN_$targetBlockName.NAME") cFB.network.dataConnections.add(cDDeserializeName) val cDMappingDispatch = factory.createFBNetworkConnection(EntryKind.DATA) cDMappingDispatch.sourceReference.setFQName("MAPPING") - cDMappingDispatch.targetReference.setFQName("DISPATCH_IN_${declaration.name}.MAPPING") + cDMappingDispatch.targetReference.setFQName("DISPATCH_IN_$targetBlockName.MAPPING") cFB.network.dataConnections.add(cDMappingDispatch) declaration.outputParameters.forEach { val cEDispatchRes = factory.createFBNetworkConnection(EntryKind.DATA) - cEDispatchRes.sourceReference.setFQName("DISPATCH_IN_${declaration.name}.${it.name}") + cEDispatchRes.sourceReference.setFQName("DISPATCH_IN_$targetBlockName.${it.name}") cEDispatchRes.targetReference.setFQName(it.name) cFB.network.dataConnections.add(cEDispatchRes) } @@ -398,7 +422,7 @@ companion object { HMIInterfaceTypeGenerator.CONNECTION_TYPES.forEach { val cDDispatch = factory.createFBNetworkConnection(EntryKind.DATA) cDDispatch.sourceReference.setFQName("JSON_DESERIALIZER.${it.name}_VALUE") - cDDispatch.targetReference.setFQName("DISPATCH_IN_${declaration.name}.${it.name}_VALUE") + cDDispatch.targetReference.setFQName("DISPATCH_IN_$targetBlockName.${it.name}_VALUE") cFB.network.dataConnections.add(cDDispatch) } } diff --git a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/ParameterDeclarationPrinter.kt b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/ParameterDeclarationPrinter.kt index 937c415bd..277e6b304 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/ParameterDeclarationPrinter.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/iec61499/stringify/ParameterDeclarationPrinter.kt @@ -1,12 +1,19 @@ package org.fbme.lib.iec61499.stringify import org.fbme.lib.iec61499.declarations.ParameterDeclaration +import org.fbme.lib.st.types.ArrayType import org.jdom.Element class ParameterDeclarationPrinter(declaration: ParameterDeclaration) : DeclarationPrinterBase(declaration, "VarDeclaration") { override fun printDeclarationBody(element: Element) { - element.setAttribute("Type", requireNotNull(this.element.type).stringify()) + val type = requireNotNull(this.element.type) + if (type is ArrayType) { + element.setAttribute("ArraySize", requireNotNull(type.dimensions).stringify()) + element.setAttribute("Type", requireNotNull(type.baseType).stringify()) + } else { + element.setAttribute("Type", type.stringify()) + } val initialValue = this.element.initialValue if (initialValue != null) { element.setAttribute("InitialValue", STPrinter.printLiteral(initialValue)) diff --git a/code/library/src/main/kotlin/org/fbme/lib/st/STFactory.kt b/code/library/src/main/kotlin/org/fbme/lib/st/STFactory.kt index cb1503647..0720a1de6 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/st/STFactory.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/st/STFactory.kt @@ -3,7 +3,7 @@ package org.fbme.lib.st import org.fbme.lib.common.Identifier import org.fbme.lib.st.expressions.* import org.fbme.lib.st.statements.* -import org.fbme.lib.st.types.DerivedType +import org.fbme.lib.st.types.* interface STFactory { fun createArrayVariable(): ArrayVariable @@ -25,4 +25,9 @@ interface STFactory { fun createReturnStatement(): ReturnStatement fun createWhileStatement(): WhileStatement fun createDerivedType(identifier: Identifier, debugName: String?): DerivedType + fun createArrayType(): ArrayType + fun createArrayTypeSizes(): ArrayTypeSizes + fun createArrayTypeSubranges(): ArrayTypeSubranges + fun createSize(): Size + fun createSubrange(): Subrange } diff --git a/code/library/src/main/kotlin/org/fbme/lib/st/types/ArrayType.kt b/code/library/src/main/kotlin/org/fbme/lib/st/types/ArrayType.kt new file mode 100644 index 000000000..019a707b7 --- /dev/null +++ b/code/library/src/main/kotlin/org/fbme/lib/st/types/ArrayType.kt @@ -0,0 +1,10 @@ +package org.fbme.lib.st.types + +import org.fbme.lib.common.ContainedElement + +interface ArrayType: NonGenericType, ContainedElement { + var baseType: NonGenericType? + var dimensions: ArrayTypeDimensions? + + override fun stringify() = "ARRAY [${dimensions?.stringify()}] OF ${baseType?.stringify()}" +} \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/st/types/ArrayTypeDimensions.kt b/code/library/src/main/kotlin/org/fbme/lib/st/types/ArrayTypeDimensions.kt new file mode 100644 index 000000000..01a85eb72 --- /dev/null +++ b/code/library/src/main/kotlin/org/fbme/lib/st/types/ArrayTypeDimensions.kt @@ -0,0 +1,8 @@ +package org.fbme.lib.st.types + +import org.fbme.lib.common.ContainedElement + +interface ArrayTypeDimensions : ContainedElement { + override val container: ArrayType? + fun stringify(): String +} diff --git a/code/library/src/main/kotlin/org/fbme/lib/st/types/ArrayTypeSizes.kt b/code/library/src/main/kotlin/org/fbme/lib/st/types/ArrayTypeSizes.kt new file mode 100644 index 000000000..347ad9c0b --- /dev/null +++ b/code/library/src/main/kotlin/org/fbme/lib/st/types/ArrayTypeSizes.kt @@ -0,0 +1,7 @@ +package org.fbme.lib.st.types + +interface ArrayTypeSizes : ArrayTypeDimensions { + val sizes: MutableList + + override fun stringify() = sizes.joinToString { it.value.toString() } +} \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/st/types/ArrayTypeSubranges.kt b/code/library/src/main/kotlin/org/fbme/lib/st/types/ArrayTypeSubranges.kt new file mode 100644 index 000000000..213d156eb --- /dev/null +++ b/code/library/src/main/kotlin/org/fbme/lib/st/types/ArrayTypeSubranges.kt @@ -0,0 +1,7 @@ +package org.fbme.lib.st.types + +interface ArrayTypeSubranges : ArrayTypeDimensions { + val subranges: MutableList + + override fun stringify() = subranges.joinToString { "${it.from}..${it.to}" } +} \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/st/types/DerivedType.kt b/code/library/src/main/kotlin/org/fbme/lib/st/types/DerivedType.kt index b7242e546..bef13b02b 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/st/types/DerivedType.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/st/types/DerivedType.kt @@ -2,7 +2,7 @@ package org.fbme.lib.st.types import org.fbme.lib.common.Identifier -interface DerivedType : DataType { +interface DerivedType : NonGenericType { val identifier: Identifier val declaration: DataTypeDeclaration? } diff --git a/code/library/src/main/kotlin/org/fbme/lib/st/types/ElementaryType.kt b/code/library/src/main/kotlin/org/fbme/lib/st/types/ElementaryType.kt index 4c18644e4..fff0eb3d0 100644 --- a/code/library/src/main/kotlin/org/fbme/lib/st/types/ElementaryType.kt +++ b/code/library/src/main/kotlin/org/fbme/lib/st/types/ElementaryType.kt @@ -1,6 +1,6 @@ package org.fbme.lib.st.types -enum class ElementaryType : DataType { +enum class ElementaryType : NonGenericType { BOOL, BYTE, DT, DWORD, DATE_AND_TIME, DATE, DINT, INT, LINT, SINT, UDINT, UINT, ULINT, USINT, LREAL, LWORD, REAL, STRING, TOD, TIME_OF_DAY, TIME, WSTRING, WORD; override fun stringify(): String { diff --git a/code/library/src/main/kotlin/org/fbme/lib/st/types/NonGenericType.kt b/code/library/src/main/kotlin/org/fbme/lib/st/types/NonGenericType.kt new file mode 100644 index 000000000..d66e2156b --- /dev/null +++ b/code/library/src/main/kotlin/org/fbme/lib/st/types/NonGenericType.kt @@ -0,0 +1,3 @@ +package org.fbme.lib.st.types + +interface NonGenericType: DataType \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/st/types/Size.kt b/code/library/src/main/kotlin/org/fbme/lib/st/types/Size.kt new file mode 100644 index 000000000..c267c4d92 --- /dev/null +++ b/code/library/src/main/kotlin/org/fbme/lib/st/types/Size.kt @@ -0,0 +1,7 @@ +package org.fbme.lib.st.types + +import org.fbme.lib.common.ContainedElement + +interface Size : ContainedElement { + var value: Int +} \ No newline at end of file diff --git a/code/library/src/main/kotlin/org/fbme/lib/st/types/Subrange.kt b/code/library/src/main/kotlin/org/fbme/lib/st/types/Subrange.kt new file mode 100644 index 000000000..4615a367e --- /dev/null +++ b/code/library/src/main/kotlin/org/fbme/lib/st/types/Subrange.kt @@ -0,0 +1,8 @@ +package org.fbme.lib.st.types + +import org.fbme.lib.common.ContainedElement + +interface Subrange: ContainedElement { + var from: Int + var to: Int +} diff --git a/code/nxt-integration/solutions/org.fbme.integration.nxt/lib/nxt-integration.jar b/code/nxt-integration/solutions/org.fbme.integration.nxt/lib/nxt-integration.jar new file mode 100644 index 0000000000000000000000000000000000000000..b162067037eaf5eb2fe6c93087598b731bf48cc4 GIT binary patch literal 60137 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

^LR{+??-q!8s^A=E-i$;Ud zlK&P-+_@V9Il~Tt$gDyE?~B1LJ6LO$f&J}- zvalxj+8!u07TX$|{^V4h8W7=2K!mGpvhx|xOAD2%<0z18=U>whx819oYQ3kY|2V4#uTL?a6rbN*~T z!Aca`9)o~e5=vAyw8qwEOWD+48x8LSAm$A+%=x?`|ktw*>(Ti1Y6k@6Y!icVW2iZ=g4=f&`lbKA~e( zZ3;GM+d9`GXw!KeU?lK91nWq&DcfZaM%vD<1YjQE8tQc(J*oc>Jk~0z!92j_pX)r* zGyiKI;1U>cF({Y>xRh<3gk|n_Nvs#PfwhC{de*f+&fl)~UtA70abuMam;kuyV4cA2 zr~fs9UuzJ+u;7L4>#)y?whOz>W$j>S@Y?8g=)q_I3-s0_5;%>#j>hxXcG1=+WH%(0 z!DfN;JL^C*&D#h1d87L|O#Mg;u3fu$R_k04p|3 X5^1ZGd{-_A5riz$0tZOp2{Qiy%__G6 literal 0 HcmV?d00001 diff --git a/code/nxt-integration/src/test/kotlin/org/fbme/integration/nxt/importer/BasicFbTypeNxtImporterTest.kt b/code/nxt-integration/src/test/kotlin/org/fbme/integration/nxt/importer/BasicFbTypeNxtImporterTest.kt index 5ed04f52e..7fcd20ec2 100644 --- a/code/nxt-integration/src/test/kotlin/org/fbme/integration/nxt/importer/BasicFbTypeNxtImporterTest.kt +++ b/code/nxt-integration/src/test/kotlin/org/fbme/integration/nxt/importer/BasicFbTypeNxtImporterTest.kt @@ -1,14 +1,11 @@ package org.fbme.integration.nxt.importer import org.fbme.ide.platform.testing.PlatformTestBase -import org.fbme.ide.platform.testing.PlatformTestRunner import org.fbme.lib.iec61499.declarations.BasicFBTypeDeclaration import org.fbme.lib.st.types.ElementaryType import org.junit.Assert import org.junit.Test -import org.junit.runner.RunWith -@RunWith(PlatformTestRunner::class) class BasicFbTypeNxtImporterTest : PlatformTestBase() { @Test fun parseTest1() { diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/IdeaTestEnvironment.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/IdeaTestEnvironment.kt new file mode 100644 index 000000000..2f0d2d5c5 --- /dev/null +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/IdeaTestEnvironment.kt @@ -0,0 +1,43 @@ +package org.fbme.ide.platform.testing + +import jetbrains.mps.tool.environment.EnvironmentConfig +import jetbrains.mps.tool.environment.IdeaEnvironment +import jetbrains.mps.util.PathManager +import java.io.File +import java.nio.file.Path + +object IdeaTestEnvironment { + + init { + System.setProperty("ide.widget.toolbar", "false") + System.setProperty("ide.cancellation.propagate", "false") + } + + val ourEnvironment by lazy { + val config = EnvironmentConfig.defaultConfig() + .withBuildPlugin() + .addDistributedPlugin("mps-execution-api", "jetbrains.mps.execution.api") + .addDistributedPlugin("mps-execution-configurations", "jetbrains.mps.execution.configurations") + .addFbmePlugin("fbme.library") + .addFbmePlugin("fbme.language") + .addFbmePlugin("fbme.platform") + .addFbmePlugin("fbme.richediting") + .addFbmePlugin("fbme.scenes") + .addFbmePlugin("fbme.formalfb") + .addFbmePlugin("fbme.integration.nxt") + val environment = IdeaEnvironment(config) + environment.init() + environment + } + + private fun libPath(relative: String) = Path.of(relative).toAbsolutePath().normalize().toString() + + private fun EnvironmentConfig.addDistributedPlugin(folder: String, id: String): EnvironmentConfig { + val preinstalledPluginFolder = File(PathManager.getPreInstalledPluginsPath()) + return addPlugin(File(preinstalledPluginFolder, folder).absolutePath, id) + } + + private fun EnvironmentConfig.addFbmePlugin(id: String): EnvironmentConfig { + return addPlugin(libPath("../../build/dist-plugins/$id"), id) + } +} \ No newline at end of file diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/LoadFrom.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/LoadFrom.kt deleted file mode 100644 index 0c3d4df35..000000000 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/LoadFrom.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.fbme.ide.platform.testing - -annotation class LoadFrom( - val module: String -) diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/PlatformTestBase.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/PlatformTestBase.kt index 2b695a77a..b3a2fb13b 100644 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/PlatformTestBase.kt +++ b/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/PlatformTestBase.kt @@ -87,20 +87,16 @@ abstract class PlatformTestBase { lateinit var project: Project - @JvmStatic - var environment: Environment? = null - @JvmStatic @BeforeClass fun initProject() { - project = environment!!.createEmptyProject() - PlatformRepositoryProvider.init(project) + project = IdeaTestEnvironment.ourEnvironment.createEmptyProject() } @JvmStatic @AfterClass fun disposeProject() { - environment!!.closeProject(project) + IdeaTestEnvironment.ourEnvironment.closeProject(project) } } } diff --git a/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/PlatformTestRunner.kt b/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/PlatformTestRunner.kt deleted file mode 100644 index 1bf26489a..000000000 --- a/code/platform/src/main/kotlin/org/fbme/ide/platform/testing/PlatformTestRunner.kt +++ /dev/null @@ -1,148 +0,0 @@ -package org.fbme.ide.platform.testing - -import jetbrains.mps.module.ReloadableModule -import jetbrains.mps.smodel.MPSModuleRepository -import jetbrains.mps.smodel.ModelAccessHelper -import jetbrains.mps.tool.environment.Environment -import jetbrains.mps.tool.environment.EnvironmentConfig -import jetbrains.mps.tool.environment.IdeaEnvironment -import jetbrains.mps.util.PathManager -import org.junit.runner.notification.RunNotifier -import org.junit.runners.BlockJUnit4ClassRunner -import org.junit.runners.model.FrameworkMethod -import org.junit.runners.model.InitializationError -import org.junit.runners.model.Statement -import java.io.File -import java.lang.reflect.Field -import java.net.MalformedURLException -import java.net.URLClassLoader -import java.nio.file.Path -import java.util.concurrent.ConcurrentHashMap - -class PlatformTestRunner(clazz: Class<*>) : BlockJUnit4ClassRunner(loadFromCustomClassloader(clazz)) { - - private val runnerEnvironment = runnerEnvironments.getValue(loadFromModuleName(clazz)) - - override fun withBefores(method: FrameworkMethod, target: Any, statement: Statement): Statement { - val original = super.withBefores(method, target, statement) - return object : Statement() { - @Throws(Throwable::class) - override fun evaluate() { - original.evaluate() - } - } - } - - // Runs junit tests in a separate thread using the custom class loader - override fun run(notifier: RunNotifier) { - val runnable = Runnable { super.run(notifier) } - val thread = Thread(runnable) - thread.contextClassLoader = runnerEnvironment.classLoader - thread.start() - try { - thread.join() - } catch (e: InterruptedException) { - throw RuntimeException(e) - } - } - - class RunnerEnvironment(val classLoader: PlatformTestClassloader) - - class PlatformTestClassloader(private val myParentModule: ReloadableModule) : URLClassLoader( - arrayOf( - File("build/classes/java/test").toURI().toURL(), - File("build/classes/kotlin/test").toURI().toURL(), - File("build/resources/test").toURI().toURL() - ), - null - ) { - @Throws(ClassNotFoundException::class) - override fun loadClass(name: String): Class<*> { - try { - return super.loadClass(name) - } catch (ignored: LinkageError) { - } catch (ignored: ClassNotFoundException) { - } - return myParentModule.getClass(name) - } - } - - companion object { - private val runnerEnvironments = ConcurrentHashMap() - - @Throws(InitializationError::class) - private fun loadFromCustomClassloader(clazz: Class<*>): Class<*> { - return try { - val moduleName = loadFromModuleName(clazz) - initializeRunnerEnvironment(moduleName) - Class.forName(clazz.name, true, runnerEnvironments.getValue(moduleName).classLoader) - } catch (e: ClassNotFoundException) { - throw InitializationError(e) - } catch (e: MalformedURLException) { - throw InitializationError(e) - } catch (e: NoSuchFieldException) { - throw InitializationError(e) - } - } - - private fun loadFromModuleName(clazz: Class<*>): String { - val loadFrom = clazz.getAnnotation(LoadFrom::class.java) - return loadFrom?.module ?: "org.fbme.ide.iec61499.adapter" - } - - @Throws(MalformedURLException::class, ClassNotFoundException::class, NoSuchFieldException::class) - fun initializeRunnerEnvironment(moduleName: String) { - runnerEnvironments.getOrPut(moduleName) { - val config = EnvironmentConfig.defaultConfig() - .withBuildPlugin() - .addDistributedPlugin("mps-execution-api", "jetbrains.mps.execution.api") - .addDistributedPlugin("mps-execution-configurations", "jetbrains.mps.execution.configurations") - .addFbmePlugin("fbme.library") - .addFbmePlugin("fbme.language") - .addFbmePlugin("fbme.platform") - .addFbmePlugin("fbme.richediting") - .addFbmePlugin("fbme.scenes") - .addFbmePlugin("fbme.formalfb") - .addFbmePlugin("fbme.integration.nxt") - val environment = IdeaEnvironment(config) - environment.init() - val repository = MPSModuleRepository.getInstance() - val parentModule = ModelAccessHelper(repository.modelAccess).runReadAction { - val platformModule = repository.modules.first { it.moduleName == moduleName } as ReloadableModule - try { - platformModule - .getClass("org.fbme.ide.iec61499.repository.MpsBridgeImpl") - .getMethod("install") - .invoke(null) - } catch (e: Exception) { - throw RuntimeException("Bridge not installed", e) - } - platformModule - } - val classloader = PlatformTestClassloader(parentModule) - val platformTestClass = - Class.forName("org.fbme.ide.platform.testing.PlatformTestBase", true, classloader) - val field = platformTestClass.getDeclaredField("environment") - field.isAccessible = true - field.set(null, environment) - RunnerEnvironment(classloader) - } - } - - private fun libPath(relative: String) = Path.of(relative).toAbsolutePath().normalize().toString() - - private fun EnvironmentConfig.addDistributedPlugin(folder: String, id: String): EnvironmentConfig { - val preinstalledPluginFolder = File(PathManager.getPreInstalledPluginsPath()) - return addPlugin(File(preinstalledPluginFolder, folder).absolutePath, id) - } - - private fun EnvironmentConfig.addFbmePlugin(id: String): EnvironmentConfig { - return addPlugin(libPath("../../build/dist-plugins/$id"), id) - } - - init { - System.setProperty("ide.widget.toolbar", "false") - System.setProperty("ide.cancellation.propagate", "false") - } - } -} diff --git a/code/platform/src/test/kotlin/org/fbme/ide/platform/adapters/FBNetworkConverterTest.kt b/code/platform/src/test/kotlin/org/fbme/ide/platform/adapters/FBNetworkConverterTest.kt index c880cf27f..70a49df69 100644 --- a/code/platform/src/test/kotlin/org/fbme/ide/platform/adapters/FBNetworkConverterTest.kt +++ b/code/platform/src/test/kotlin/org/fbme/ide/platform/adapters/FBNetworkConverterTest.kt @@ -1,13 +1,10 @@ package org.fbme.ide.platform.adapters import org.fbme.ide.platform.testing.PlatformTestBase -import org.fbme.ide.platform.testing.PlatformTestRunner import org.junit.Assert import org.junit.Test -import org.junit.runner.RunWith // TODO implement real tests -@RunWith(PlatformTestRunner::class) class FBNetworkConverterTest : PlatformTestBase() { @Test diff --git a/code/platform/src/test/kotlin/org/fbme/ide/platform/adapters/FBResolveTest.kt b/code/platform/src/test/kotlin/org/fbme/ide/platform/adapters/FBResolveTest.kt index 38fb154f3..d0cfd13c0 100644 --- a/code/platform/src/test/kotlin/org/fbme/ide/platform/adapters/FBResolveTest.kt +++ b/code/platform/src/test/kotlin/org/fbme/ide/platform/adapters/FBResolveTest.kt @@ -4,7 +4,6 @@ import jetbrains.mps.smodel.tempmodel.TempModuleOptions import jetbrains.mps.smodel.tempmodel.TemporaryModels import org.fbme.ide.iec61499.repository.PlatformElement import org.fbme.ide.platform.testing.PlatformTestBase -import org.fbme.ide.platform.testing.PlatformTestRunner import org.fbme.lib.iec61499.declarations.BasicFBTypeDeclaration import org.fbme.lib.iec61499.declarations.CompositeFBTypeDeclaration import org.junit.Test @@ -12,7 +11,6 @@ import org.junit.runner.RunWith import kotlin.test.assertEquals import kotlin.test.assertTrue -@RunWith(PlatformTestRunner::class) class FBResolveTest : PlatformTestBase() { @Test diff --git a/code/platform/src/test/kotlin/org/fbme/ide/platform/adapters/IEC61499FactoryTest.kt b/code/platform/src/test/kotlin/org/fbme/ide/platform/adapters/IEC61499FactoryTest.kt index 931b8f271..45805a3fd 100644 --- a/code/platform/src/test/kotlin/org/fbme/ide/platform/adapters/IEC61499FactoryTest.kt +++ b/code/platform/src/test/kotlin/org/fbme/ide/platform/adapters/IEC61499FactoryTest.kt @@ -1,15 +1,12 @@ package org.fbme.ide.platform.adapters import org.fbme.ide.platform.testing.PlatformTestBase -import org.fbme.ide.platform.testing.PlatformTestRunner import org.fbme.lib.common.StringIdentifier import org.fbme.lib.iec61499.declarations.AlgorithmLanguage import org.fbme.lib.iec61499.fbnetwork.EntryKind import org.junit.Assert import org.junit.Test -import org.junit.runner.RunWith -@RunWith(PlatformTestRunner::class) class IEC61499FactoryTest : PlatformTestBase() { @Test fun createAdapterTypeDeclaration() { diff --git a/samples/sandbox/solutions/org.fbme.ide.eccSandbox/models/org.fbme.ide.eccSandbox/null.fbt b/samples/sandbox/solutions/org.fbme.ide.eccSandbox/models/org.fbme.ide.eccSandbox/null.fbt new file mode 100644 index 000000000..a5c51024e --- /dev/null +++ b/samples/sandbox/solutions/org.fbme.ide.eccSandbox/models/org.fbme.ide.eccSandbox/null.fbt @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/samples/sandbox/solutions/org.fbme.ide.sandbox/models/org.fbme.iec61499.lang.sandbox.standard/TestArrayVariables.fbt b/samples/sandbox/solutions/org.fbme.ide.sandbox/models/org.fbme.iec61499.lang.sandbox.standard/TestArrayVariables.fbt new file mode 100644 index 000000000..48fc70e73 --- /dev/null +++ b/samples/sandbox/solutions/org.fbme.ide.sandbox/models/org.fbme.iec61499.lang.sandbox.standard/TestArrayVariables.fbt @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/statistics-plugin/solutions/org.fbme.samples.statisticsPlugin/lib/statistics-plugin.jar b/samples/statistics-plugin/solutions/org.fbme.samples.statisticsPlugin/lib/statistics-plugin.jar new file mode 100644 index 0000000000000000000000000000000000000000..a57506d0168cd1596976e9fe8b444297e3a0368e GIT binary patch literal 4088 zcma)9c|4SB8y zzEn$N1T>Ac)L0ucqm_ zUUf)Lp8{p2t#0aFV2II#!lyB4wE@$;Kc#i8cT8+rNh3IR6DK*Dj`_K6qH+(h+h=(_ zQsFBjn)OtvQVjUmZ3AaLv1IWX9;fcqg-(^DXwa6E9aU zV1;I2hse?Ayn0Qe+zl|wInKI%+&;&d>6du<`>CLrft*AB^TR<+K%C%&(L-EkgiFOc&)p))U_Yr@R&;#Is(EK_K*F^-P80AD<^TXU!^v5fL9MM#P&Bdh5@Ta8y`fy{*Sf;f%Q(oW+)!IGL8op(CH$% z;hTqUZjH%_x-hZx2yR%kYTUjau=qkXw{+o7Z{Q~5E1xBlZ=j=J-;p`)Z3D5Op|_M! zHt*N|R~59ty*^)*Nm4rah4kPz%yku$V#h?LYnO;zFRX&~Q`SI21GcrulSF9m*hgu$ z!)+bX;Ygzq_{}!YgfIU2aq8i^CJ7Qo=4P2@xxz+L*BQ&}Cj8>NJSG)xY_KkEHD4s+o1YIh zPv1IA{~78`evWDPG!i%O4T-(meW<}o9UO|;ur>k(FKt<1Hm<6ep357@OsE*vD{n+s zcK3bQUm2-XDtgDncz&=a=nSrh_kvjUs$Ad7=d5i8Z?-G9(+;hEf>t>2(^+O0hJMPP zh^}gk7ja|aBfQ{My|zd{Z!>a1Opx8~(FXI6YD_DR{JBWJ$4mggdv*Xo?O$rhFN?>w zqET&k9>ZnB7h#s|-tRWrcbb)sRSyW-5fzW$`3(rNmPJ`a8=ijE6+G&jHrF;6%V~J9 zu`vj5=_mBC*4-$uBW5zDQ27f$;r64(7Wl_WjhrHsC+s4BY5!x7u@%B%SoezV=Y^SD zjlPkbWDBgo8Re7#G9BAXxMEVFI`4QNn$MhUa6qE(guujzJ>QNxe_L9pY?8jZRNU3* ze)vH9fhhkEXrpJ!a09ez5=Q+Y`HiB0l5F9fGzHV4Jma8fp zb(u9@2$Tj9G$Z-f6-;J_^QS+@XGnX?_Jm3~c6fiiAYFF0TzggXVOHV^pmlY7%<8*l zsM4#`BZF4ji`Qz3uVqGQ|6awmo|+48&BdmkOOnQS+CQ5`IcwFaKRvzJSR&hECEpo- zOFOe2>vGbK7n)%fS+C8~AB!9j_l#n=s8x14*Q%yxnVi{fy=lRgHFy;yyCd)#cc+_s7I>8L%zmvrL^9;q(=MLYdgBtCSJ67MHLf`cd^K{4)~2or6a1B^ zp#mtbFjfm?uD(iV$^yTirFTvbX2`64UxC*OF7ZG-CC(Zjf1BPoCJ~>ISX(r4qGDg* z!|F9Xyrde|q1C2RSeS6Z*#;kN$KzzRBv7G{l1BZ<&vK9f0Jv7o=bLk$NeyHH$#}7{2M<~hr zqmubSQe)y(xk_)n-rRHQ3J4oM&y#H$L!@`KoejmRtDINEx!w2oC(vJZ{=DC0N(xxN zKO(s&m|=x9DkC8!uN^2+R_H@N%^Q`kaiOjR_nW2Rw3T+b2BHl)bdmUeW9AV)CjggN z>N?6RC8@-DE{d{n4s|I=$T+k%APlV0Cu0-Qk);w4Tl{!qbaC4Ji8B8n;$2K#wp}3k zReA;l1?Fz? z<+n`=gDg4}j)>$`$lMh6FzA@`gV1unuN-%uPV!>sbDuzi(n@5j!p9?0DC(;Du^~bB z*HvMzF^Y3WTI8;&;O3lm_RF(mr`#uV`Ne>Ov>N%jaT&VXs{p$4`LXc1-BDYH>WgGN*zTMTAu#F)V3?U*pE`M4X7kw}MM;)upvjVMI ze4?Ususe^ zm!j2l7n2pX8Gf7teAIJ*uULy+hzz?H&4eSAH#`GgSfhBWrkRFj3J~!>665T?iP8q!ye;FH;H7qNAkAxy+is4 ztlhQi>pROPJ_C1E*#AUUB0TUgfMnT7T1&M3V=>i}87KV1rmFR;#|R59`(KPHX$w`e z@``{pKvP}J_fn3cakeYm?yO{IW#}pF>+Bo8K36=d^wG2YBVx^NhstIcosKO|0a zU_2DdcTL(KA(|qEwR!qLl2VS?35k{Cm!pmzGrv!EHWm{@imS+~j5 zTq_j*=}oHTg6;Lp5Ve_P*_%ngN&fPadE%w#q~)Rhy#&FaI2KHTW`sV%RjTMIPV%%F z&r}|EOL%kfQUukN(9v@<{BJizeWTPk0GQXK-M_!w-LT!>h?Y!?qNrok`8(_Eoksh z)a5_mKN+dLm}o}oThN#kso(NmO#ii2y9syOs=p|UqA>p-;V=87zniSxn!DSuADU07 v&DmZL@k_C9P1(;Pzxv02h%~bP^mYCC