├── README.md ├── pom.xml ├── .gitignore ├── src └── main │ └── java │ └── org │ └── pragmaticindustries │ └── Example.java └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | # Simple Example of how to use Apache PLC4X together with Eclipse Ditto 2 | 3 | This example shows how to build a minimal digital twin for a PLC. 4 | 5 | ## Setup 6 | 7 | * Before starting, you need to start the docker-compose based local Ditto deployment (with all default settings). 8 | * To build the project simply run `mvn clean install`. 9 | * Then you are good to go and can execute `Example`. 10 | 11 | ## Configuration 12 | 13 | ### Mocked 14 | 15 | Two sets of configurations are provided. 16 | If you use the mock 17 | ``` 18 | private static final String PLC4X_FIELD_NAME = "pressure"; 19 | private static final String PLC4X_PLC_ADDRESS = "mock:plc"; 20 | private static final String PLC4X_FIELD_ADDRESS = "%DB:xxx"; 21 | ``` 22 | the program will start a "mocked" plc which always returns a random value between 0 and 100. 23 | 24 | ### Siemens S7 25 | 26 | If you have a Siemens S7 at hand you can also use this set of configurations 27 | 28 | ``` 29 | private static final String PLC4X_FIELD_NAME = "pressure"; 30 | private static final String PLC4X_PLC_ADDRESS = "s7://192.168.167.210/1/1"; 31 | private static final String PLC4X_FIELD_ADDRESS = "%DB:xxx"; 32 | ``` 33 | and enter a valid PLC IP as well as a valid field address (in PLC4X syntax). -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.pragmaticindustries 8 | ditto-plc4x-example 9 | 1.0.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 16 | 8 17 | 8 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.apache.plc4x 26 | plc4j-api 27 | 0.6.0.0 28 | 29 | 30 | org.apache.plc4x 31 | plc4j-driver-s7 32 | 0.6.0 33 | 34 | 35 | org.apache.plc4x 36 | plc4j-protocol-driver-base-test 37 | 0.6.0 38 | 39 | 40 | org.apache.plc4x 41 | plc4j-scraper 42 | 0.6.0 43 | 44 | 45 | org.eclipse.ditto 46 | ditto-client 47 | 1.1.0-M2 48 | 49 | 50 | org.slf4j 51 | slf4j-simple 52 | 1.7.30 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/**/usage.statistics.xml 10 | .idea/**/dictionaries 11 | .idea/**/shelf 12 | 13 | # Generated files 14 | .idea/**/contentModel.xml 15 | 16 | # Sensitive or high-churn files 17 | .idea/**/dataSources/ 18 | .idea/**/dataSources.ids 19 | .idea/**/dataSources.local.xml 20 | .idea/**/sqlDataSources.xml 21 | .idea/**/dynamic.xml 22 | .idea/**/uiDesigner.xml 23 | .idea/**/dbnavigator.xml 24 | 25 | # Gradle 26 | .idea/**/gradle.xml 27 | .idea/**/libraries 28 | 29 | # Gradle and Maven with auto-import 30 | # When using Gradle or Maven with auto-import, you should exclude module files, 31 | # since they will be recreated, and may cause churn. Uncomment if using 32 | # auto-import. 33 | # .idea/artifacts 34 | # .idea/compiler.xml 35 | # .idea/modules.xml 36 | # .idea/*.iml 37 | # .idea/modules 38 | # *.iml 39 | # *.ipr 40 | 41 | # CMake 42 | cmake-build-*/ 43 | 44 | # Mongo Explorer plugin 45 | .idea/**/mongoSettings.xml 46 | 47 | # File-based project format 48 | *.iws 49 | 50 | # IntelliJ 51 | out/ 52 | 53 | # mpeltonen/sbt-idea plugin 54 | .idea_modules/ 55 | 56 | # JIRA plugin 57 | atlassian-ide-plugin.xml 58 | 59 | # Cursive Clojure plugin 60 | .idea/replstate.xml 61 | 62 | # Crashlytics plugin (for Android Studio and IntelliJ) 63 | com_crashlytics_export_strings.xml 64 | crashlytics.properties 65 | crashlytics-build.properties 66 | fabric.properties 67 | 68 | # Editor-based Rest Client 69 | .idea/httpRequests 70 | 71 | # Android studio 3.1+ serialized cache file 72 | .idea/caches/build_file_checksums.ser 73 | 74 | ### Maven template 75 | target/ 76 | pom.xml.tag 77 | pom.xml.releaseBackup 78 | pom.xml.versionsBackup 79 | pom.xml.next 80 | release.properties 81 | dependency-reduced-pom.xml 82 | buildNumber.properties 83 | .mvn/timing.properties 84 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 85 | .mvn/wrapper/maven-wrapper.jar 86 | 87 | ### Java template 88 | # Compiled class file 89 | *.class 90 | 91 | # Log file 92 | *.log 93 | 94 | # BlueJ files 95 | *.ctxt 96 | 97 | # Mobile Tools for Java (J2ME) 98 | .mtj.tmp/ 99 | 100 | # Package Files # 101 | *.jar 102 | *.war 103 | *.nar 104 | *.ear 105 | *.zip 106 | *.tar.gz 107 | *.rar 108 | 109 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 110 | hs_err_pid* 111 | 112 | -------------------------------------------------------------------------------- /src/main/java/org/pragmaticindustries/Example.java: -------------------------------------------------------------------------------- 1 | package org.pragmaticindustries; 2 | 3 | import org.apache.commons.lang3.tuple.Pair; 4 | import org.apache.plc4x.java.PlcDriverManager; 5 | import org.apache.plc4x.java.api.PlcConnection; 6 | import org.apache.plc4x.java.api.exceptions.PlcConnectionException; 7 | import org.apache.plc4x.java.api.messages.PlcSubscriptionEvent; 8 | import org.apache.plc4x.java.api.model.PlcConsumerRegistration; 9 | import org.apache.plc4x.java.api.model.PlcSubscriptionHandle; 10 | import org.apache.plc4x.java.api.types.PlcResponseCode; 11 | import org.apache.plc4x.java.base.messages.items.BaseDefaultFieldItem; 12 | import org.apache.plc4x.java.base.messages.items.DefaultIntegerFieldItem; 13 | import org.apache.plc4x.java.mock.MockDevice; 14 | import org.apache.plc4x.java.mock.PlcMockConnection; 15 | import org.eclipse.ditto.client.DittoClient; 16 | import org.eclipse.ditto.client.DittoClients; 17 | import org.eclipse.ditto.client.configuration.BasicAuthenticationConfiguration; 18 | import org.eclipse.ditto.client.configuration.MessagingConfiguration; 19 | import org.eclipse.ditto.client.configuration.WebSocketMessagingConfiguration; 20 | import org.eclipse.ditto.client.messaging.AuthenticationProviders; 21 | import org.eclipse.ditto.client.messaging.internal.WebSocketMessagingProvider; 22 | import org.eclipse.ditto.model.things.Feature; 23 | import org.eclipse.ditto.model.things.Features; 24 | import org.eclipse.ditto.model.things.Thing; 25 | import org.eclipse.ditto.model.things.ThingId; 26 | import org.eclipse.ditto.signals.commands.things.exceptions.ThingNotAccessibleException; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | import java.util.Collection; 31 | import java.util.Random; 32 | import java.util.concurrent.ExecutionException; 33 | import java.util.concurrent.Executors; 34 | import java.util.concurrent.TimeUnit; 35 | import java.util.concurrent.TimeoutException; 36 | import java.util.function.Consumer; 37 | 38 | /** 39 | * Very simple Example that shows how to use Apaches PLC4X together with 40 | * Eclipse Ditto. 41 | */ 42 | public class Example { 43 | 44 | private static final Logger logger = LoggerFactory.getLogger(Example.class); 45 | 46 | private static final String DITTO_API_ENDPOINT = "ws://localhost:8080/ws/2"; 47 | private static final String DITTO_USER = "ditto"; 48 | private static final String DITTO_PASSWORD = "ditto"; 49 | private static final String THING_NAME = "org.pragmaticindustries:my-plc"; 50 | 51 | // Mocked 52 | private static final String PLC4X_FIELD_NAME = "pressure"; 53 | private static final String PLC4X_PLC_ADDRESS = "mock:plc"; 54 | private static final String PLC4X_FIELD_ADDRESS = "%DB:xxx"; 55 | 56 | // // Real Siemens Device 57 | // private static final String PLC4X_FIELD_NAME = "pressure"; 58 | // private static final String PLC4X_PLC_ADDRESS = "s7://192.168.167.210/1/1"; 59 | // private static final String PLC4X_FIELD_ADDRESS = "%DB555.DBD0:DINT"; 60 | 61 | public static void main(String[] args) throws ExecutionException, InterruptedException, PlcConnectionException { 62 | MessagingConfiguration configuration = WebSocketMessagingConfiguration.newBuilder() 63 | .endpoint(DITTO_API_ENDPOINT) 64 | .build(); 65 | WebSocketMessagingProvider provider = WebSocketMessagingProvider.newInstance(configuration, 66 | AuthenticationProviders 67 | .basic(BasicAuthenticationConfiguration.newBuilder() 68 | .username(DITTO_USER) 69 | .password(DITTO_PASSWORD) 70 | .build()), 71 | Executors.newFixedThreadPool(4)); 72 | DittoClient client = DittoClients.newInstance(provider); 73 | 74 | // Check if the Thing already exists 75 | try { 76 | client.twin().forId(ThingId.of(THING_NAME)).retrieve().get(); 77 | } catch (ExecutionException e) { 78 | if (e.getCause() instanceof ThingNotAccessibleException) { 79 | // Not existing, create 80 | logger.info("Digital Twin not found, creating new..."); 81 | Thing thing = Thing.newBuilder() 82 | .setId(ThingId.of(THING_NAME)) 83 | .setFeature("live-data") 84 | .build(); 85 | client.twin().create(thing).get(); 86 | } 87 | } 88 | 89 | // Check if the feature exists 90 | boolean present = client.twin().forId(ThingId.of(THING_NAME)).retrieve().get() 91 | .getFeatures().map(features -> features.getFeature("live-data")).isPresent(); 92 | if (!present) { 93 | logger.info("feature not present, adding feature..."); 94 | client.twin().forId(ThingId.of(THING_NAME)).setFeatures(Features.newBuilder().set(Feature.newBuilder().withId("live-data").build()).build()); 95 | } 96 | // Put Attribute in again 97 | client.twin().forId(ThingId.of(THING_NAME)).putAttribute("plc-address", PLC4X_PLC_ADDRESS).get(); 98 | 99 | // Prepare the Mock 100 | PlcDriverManager plcDriverManager = new PlcDriverManager(); 101 | if (PLC4X_PLC_ADDRESS.startsWith("mock:")) { 102 | setupMock(plcDriverManager); 103 | } 104 | 105 | // Now start the loop 106 | try (PlcConnection connection = plcDriverManager.getConnection(PLC4X_PLC_ADDRESS)) { 107 | for (int i = 1; i <= 100_000; i++) { 108 | int value = connection.readRequestBuilder().addItem("item1", PLC4X_FIELD_ADDRESS).build().execute().get().getInteger("item1"); 109 | logger.debug("Got value {} from PLC, sending", value); 110 | client.twin().forId(ThingId.of(THING_NAME)).forFeature("live-data").putProperty(PLC4X_FIELD_NAME, value).get(); 111 | logger.debug("Update in Ditto was successful"); 112 | logger.info("Current snapshot of twin: {}", client.twin().forId(ThingId.of(THING_NAME)).retrieve().get()); 113 | Thread.sleep(100); 114 | } 115 | } catch (Exception e) { 116 | e.printStackTrace(); 117 | } 118 | } 119 | 120 | private static void setupMock(PlcDriverManager plcDriverManager) throws PlcConnectionException { 121 | PlcMockConnection mockConnection = (PlcMockConnection) plcDriverManager.getConnection(PLC4X_PLC_ADDRESS); 122 | mockConnection.setDevice(new MockDevice() { 123 | @Override 124 | public Pair read(String s) { 125 | return Pair.of(PlcResponseCode.OK, new DefaultIntegerFieldItem((new Random()).nextInt(100))); 126 | } 127 | 128 | @Override 129 | public PlcResponseCode write(String s, Object o) { 130 | return null; 131 | } 132 | 133 | @Override 134 | public Pair subscribe(String s) { 135 | return null; 136 | } 137 | 138 | @Override 139 | public void unsubscribe() { 140 | 141 | } 142 | 143 | @Override 144 | public PlcConsumerRegistration register(Consumer consumer, Collection collection) { 145 | return null; 146 | } 147 | 148 | @Override 149 | public void unregister(PlcConsumerRegistration plcConsumerRegistration) { 150 | 151 | } 152 | }); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------