├── .gitignore ├── README.md ├── build ├── generic-tcp-driver-gateway │ └── pom.xml └── generic-tcp-driver-module │ ├── license.html │ └── pom.xml ├── pom.xml └── src ├── doc ├── fo-custom.xsl ├── header-config-en.png ├── html-custom.xsl ├── manual-frame.html ├── manual-styles.css ├── message-config-en.png ├── module-manual.xml └── writeback-config-en.png ├── main └── java │ └── com │ └── chitek │ ├── ignition │ └── drivers │ │ └── generictcp │ │ ├── AbstractGenericTcpDriver.java │ │ ├── GenericTcpClientDriver.java │ │ ├── GenericTcpDriver.properties │ │ ├── GenericTcpDriver_de.properties │ │ ├── GenericTcpServerDriver.java │ │ ├── IGenericTcpDriverContext.java │ │ ├── ModuleHook.java │ │ ├── configuration │ │ ├── GenericTcpClientDriverType.java │ │ ├── GenericTcpServerDriverType.java │ │ └── settings │ │ │ ├── GenericTcpClientDriverSettings.java │ │ │ ├── GenericTcpClientDriverSettings.properties │ │ │ ├── GenericTcpClientDriverSettings_de.properties │ │ │ ├── GenericTcpServerDriverSettings.java │ │ │ ├── GenericTcpServerDriverSettings.properties │ │ │ ├── GenericTcpServerDriverSettings_de.properties │ │ │ └── IGenericTcpDriverSettings.java │ │ ├── folder │ │ ├── BrowseTree.java │ │ ├── DeviceStatusFolder.java │ │ ├── FolderManager.java │ │ ├── ISubscriptionChangeListener.java │ │ ├── IndexMessageFolder.java │ │ ├── MessageDataWrapper.java │ │ ├── MessageFolder.java │ │ ├── MessageHeader.java │ │ ├── SimpleWriteFolder.java │ │ ├── StatusFolder.java │ │ └── SubscriptionUpdater.java │ │ ├── io │ │ ├── ClientEventHandler.java │ │ ├── IIoEventHandler.java │ │ ├── IMessageHandler.java │ │ ├── MessageState.java │ │ ├── NioEventHandler.java │ │ ├── NioServer.java │ │ ├── NioTcpServer.java │ │ ├── NioUdpServer.java │ │ └── TimeoutHandler.java │ │ ├── meta │ │ └── config │ │ │ ├── DriverConfig.java │ │ │ ├── DriverSettings.java │ │ │ ├── DriverSettingsPassive.java │ │ │ ├── HeaderConfig.java │ │ │ ├── HeaderTagConfig.java │ │ │ ├── IDriverSettings.java │ │ │ ├── MessageConfig.java │ │ │ ├── TagConfig.java │ │ │ ├── WritebackConfig.java │ │ │ └── ui │ │ │ ├── AbstractConfigUI.java │ │ │ ├── ConfigUiTabs.html │ │ │ ├── ConfigUiTabs.java │ │ │ ├── HeaderConfigUI.html │ │ │ ├── HeaderConfigUI.java │ │ │ ├── HeaderConfigUI.properties │ │ │ ├── HeaderConfigUI_de.properties │ │ │ ├── MessageConfigUI.html │ │ │ ├── MessageConfigUI.java │ │ │ ├── MessageConfigUI.properties │ │ │ ├── MessageConfigUI_de.properties │ │ │ ├── SessionConfig.java │ │ │ ├── WritebackConfigUI.html │ │ │ ├── WritebackConfigUI.java │ │ │ ├── WritebackConfigUI.properties │ │ │ ├── WritebackConfigUI_de.properties │ │ │ ├── res │ │ │ ├── actionlinksnewarrow.gif │ │ │ ├── close.gif │ │ │ ├── configui.css │ │ │ ├── help.gif │ │ │ ├── indicator.gif │ │ │ ├── maximize.gif │ │ │ ├── row_delete.png │ │ │ ├── row_down.png │ │ │ ├── row_insert.png │ │ │ └── row_up.png │ │ │ ├── wicket-package.properties │ │ │ └── wicket-package_de.properties │ │ ├── tags │ │ ├── ReadableArrayTag.java │ │ ├── ReadableBoolArrayTag.java │ │ ├── ReadableStringTag.java │ │ ├── ReadableTcpDriverTag.java │ │ └── WritableTag.java │ │ ├── types │ │ ├── BinaryDataType.java │ │ ├── DriverState.java │ │ ├── HeaderDataType.java │ │ ├── MessageType.java │ │ ├── OptionalDataType.java │ │ ├── QueueMode.java │ │ ├── RemoteDevice.java │ │ ├── TagLengthType.java │ │ └── WritebackDataType.java │ │ └── util │ │ ├── TestUtils.java │ │ ├── Util.java │ │ └── VariantByteBuffer.java │ ├── util │ ├── PersistentQueue.java │ └── XMLConfigParser.java │ └── wicket │ ├── FeedbackTextField.java │ ├── NonMatchStringValidator.java │ └── listeditor │ ├── EditorListItem.java │ ├── EditorSubmitLink.java │ ├── ListEditor.java │ └── UniqueListItemValidator.java └── test ├── java └── com │ └── chitek │ ├── TestUtils │ └── MockExecutor.java │ └── ignition │ └── drivers │ └── generictcp │ └── tests │ ├── DriverTestSuite.java │ ├── MockDriverContext.java │ ├── MockExecutionManager.java │ ├── TestUtils.java │ ├── config │ └── TestConfigParser.java │ ├── folders │ ├── FolderTestUtils.java │ ├── MockMessageFolder.java │ ├── MockReadItem.java │ ├── MockSubscriptionItem.java │ ├── MockWriteItem.java │ ├── TestDeviceStatusFolder.java │ ├── TestFolderManager.java │ ├── TestMessageFolder.java │ ├── TestSimpleWriteFolder.java │ └── TestSubscription.java │ └── io │ ├── TestMessageState.java │ ├── TestNioEventHandler.java │ ├── TestNioServer.java │ ├── TestNioUdpServer.java │ └── TestTimeoutHandler.java └── resources ├── log4j.properties ├── testHeaderConfig.xml ├── testHeaderConfigByte.xml ├── testHeaderConfigHandshake.xml ├── testHeaderConfigSimple.xml ├── testMessageConfig.xml ├── testMessageConfigPacketBased.xml ├── testMessageConfigPersistant.xml ├── testMessageConfigQueue.xml ├── testMessageConfigSimple.xml ├── testMessageConfigTypes.xml ├── testMessageConfigWithAge.xml └── testWritebackConfig.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /.settings 2 | .project 3 | .classpath 4 | /target 5 | /build/ 6 | /logs/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Generic TCP Driver 2 | ================== 3 | 4 | The generic TCP driver connects network devices that use a custom protocol to the Ignition(R) OPC-UA server. The communication protocol is configurable for each device. This driver does not poll data but expects the connected device to send messages on its own. 5 | 6 | The Generic TCP Driver module adds two new drivers to the Ignition TM OPC-UA Server: 7 | - Generic TCP Client Driver 8 | The client driver connects actively to a remote device. 9 | - Generic TCP Server Driver 10 | The server driver is as a passive listener. It opens a tcp-port on the server machine and waits for remote devices to establish a connection. Each driver instance allows multiple remote devices to connect. -------------------------------------------------------------------------------- /build/generic-tcp-driver-gateway/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | com.chitek.ignition 6 | generic-tcp-driver 7 | ../../pom.xml 8 | 3.0.3 9 | 10 | 11 | generic-tcp-driver-gateway 12 | generic-tcp-driver 13 | 14 | 15 | ../../target/classes 16 | ../../target/test-classes 17 | ../../src/main/java 18 | ../../src/test/java 19 | 20 | 21 | 22 | ../../src/main/java 23 | 24 | **/*.java 25 | 26 | 27 | 28 | 29 | ../../src/test/resources 30 | 31 | 32 | 33 | maven-compiler-plugin 34 | 3.8.0 35 | 36 | 10 37 | 38 | 39 | 40 | 41 | pl.project13.maven 42 | git-commit-id-plugin 43 | 2.1.7 44 | 45 | 46 | git-buildnumber 47 | 48 | revision 49 | 50 | prepare-package 51 | 52 | 53 | 54 | ${project.basedir}/.git 55 | yyyyMMddHH 56 | 57 | 58 | 59 | 60 | org.apache.maven.plugins 61 | maven-jar-plugin 62 | 2.4 63 | 64 | 65 | 66 | ${project.version} 67 | ${git.commit.id.abbrev} 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.chitek.ignition 4 | generic-tcp-driver 5 | 3.0.3 6 | generic-tcp-driver-parent 7 | pom 8 | 9 | 10 | build/generic-tcp-driver-gateway 11 | build/generic-tcp-driver-module 12 | 13 | 14 | 15 | src/main/java 16 | src/test/java 17 | 18 | ../../src/test/resources 19 | 20 | 21 | 22 | 23 | pl.project13.maven 24 | git-commit-id-plugin 25 | 2.1.7 26 | 27 | 28 | git-buildnumber 29 | 30 | revision 31 | 32 | prepare-package 33 | 34 | 35 | 36 | ${project.basedir}/.git 37 | yyyyMMddHH 38 | 39 | 40 | 41 | 42 | maven-deploy-plugin 43 | 2.7 44 | 45 | 46 | default-deploy 47 | 48 | true 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | com.inductiveautomation.ignitionsdk 58 | driver-api 59 | 8.1.0 60 | pom 61 | provided 62 | 63 | 64 | com.inductiveautomation.ignitionsdk 65 | ignition-common 66 | 8.1.0 67 | provided 68 | pom 69 | 70 | 71 | com.inductiveautomation.ignitionsdk 72 | gateway-api 73 | 8.1.0 74 | provided 75 | pom 76 | 77 | 78 | org.hamcrest 79 | hamcrest-all 80 | 1.3 81 | test 82 | 83 | 84 | junit 85 | junit 86 | 4.13.1 87 | 88 | 89 | hamcrest-core 90 | org.hamcrest 91 | 92 | 93 | test 94 | 95 | 96 | 97 | 98 | ignition-releases 99 | https://nexus.inductiveautomation.com/repository/inductiveautomation-releases 100 | 101 | false 102 | 103 | 104 | true 105 | always 106 | 107 | 108 | 109 | snapshots 110 | https://nexus.inductiveautomation.com/repository/inductiveautomation-snapshots 111 | 112 | true 113 | always 114 | 115 | 116 | false 117 | 118 | 119 | 120 | ignition-thirdparty 121 | https://nexus.inductiveautomation.com/repository/inductiveautomation-thirdparty 122 | 123 | true 124 | always 125 | 126 | 127 | false 128 | 129 | 130 | 131 | 132 | oss-sonatype 133 | oss-sonatype 134 | https://oss.sonatype.org/content/repositories/snapshots/ 135 | 136 | 137 | central 138 | central 139 | https://repo.maven.apache.org/maven2 140 | 141 | 142 | 143 | 144 | oss-sonatype 145 | oss-sonatype 146 | https://oss.sonatype.org/content/repositories/releases/ 147 | 148 | 149 | ignition-releases 150 | https://nexus.inductiveautomation.com/repository/inductiveautomation-releases 151 | 152 | true 153 | always 154 | 155 | 156 | false 157 | 158 | 159 | 160 | 161 | UTF-8 162 | http://10.224.11.101:8088 163 | 164 | -------------------------------------------------------------------------------- /src/doc/fo-custom.xsl: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | monospace 11 | 12 | 13 | page 14 | 15 | 16 | 17 | 18 | .5em 19 | 20 | 21 | .3em 22 | 23 | 24 | .8em 25 | 26 | 27 | 28 | 29 | Lucida Sans Typewriter 30 | 9pt 31 | always 32 | 33 | 34 | 35 | 36 | thin black solid 37 | #E0E0E0 38 | 39 | 40 | 41 | bold 42 | 43 | 44 | 45 | blue 46 | underline 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/doc/header-config-en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/doc/header-config-en.png -------------------------------------------------------------------------------- /src/doc/html-custom.xsl: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/doc/manual-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generic Tcp Driver Manual 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/doc/manual-styles.css: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/doc/message-config-en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/doc/message-config-en.png -------------------------------------------------------------------------------- /src/doc/writeback-config-en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/doc/writeback-config-en.png -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/GenericTcpDriver.properties: -------------------------------------------------------------------------------- 1 | GenericTcpClientDriverType.Name=Generic TCP Client Driver 2 | GenericTcpClientDriverType.Description=Connect to network devices using a configurable TCP protocol. 3 | 4 | GenericTcpServerDriverType.Name=Generic TCP/UDP Server Driver 5 | GenericTcpServerDriverType.Description=Passive TCP or UDP listener for devices using a configurable protocol. 6 | 7 | # Driver State 8 | State.Disabled=Connection disabled 9 | State.Disconnected=Device offline 10 | State.Connecting=Connecting 11 | State.Connected=Device online 12 | State.Disconnecting=Disconnecting 13 | State.Terminated=Driver instance terminated 14 | State.ConfigError=Configuration Error 15 | State.Listening=Listening for device connections 16 | State.passiveConnectedSingular=1 device connected 17 | State.passiveConnectedPlural=%d devices connected 18 | 19 | 20 | # General Errors 21 | error.xmlParseError=Configuration could not be loaded. XML Parser Exception: %s 22 | error.notBase64=Configuration could not be loaded. Input is not Base64 encoded. 23 | error.invalidDevice=Invalid remote device configuration '%s' 24 | 25 | # Configuration UI 26 | configLink=Config 27 | configurationtitle=Generic TCP Driver Configuration 28 | messageTab=Message Configuration 29 | headerTab=Message Header 30 | writebackTab=Writeback Message 31 | ConfigUI.New=New 32 | ConfigUI.ParseError=Error parsing configuration string. See gateway error log for details. 33 | ConfigUI.Error.BackupNode=Cannot modify settings on the backup redundant gateway. Please visit the master gateway 34 | ConfigUI.Error.BackupNodeSave=Cannot save settings on the backup redundant gateway. Please visit the master gateway 35 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/GenericTcpDriver_de.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/main/java/com/chitek/ignition/drivers/generictcp/GenericTcpDriver_de.properties -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/IGenericTcpDriverContext.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.concurrent.ScheduledFuture; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import org.eclipse.milo.opcua.sdk.server.nodes.UaNode; 8 | import org.eclipse.milo.opcua.sdk.server.nodes.UaNodeContext; 9 | import org.eclipse.milo.opcua.sdk.server.nodes.UaObjectNode.UaObjectNodeBuilder; 10 | import org.eclipse.milo.opcua.sdk.server.nodes.UaVariableNode.UaVariableNodeBuilder; 11 | 12 | import com.inductiveautomation.ignition.common.execution.SelfSchedulingRunnable; 13 | 14 | /** 15 | * The GenericTcpDriverContext wraps all information that has to be passed 16 | * from the driver class to message folders. 17 | * 18 | */ 19 | public interface IGenericTcpDriverContext { 20 | 21 | /** 22 | * @return 23 | * The user-assigned name for this device (driver instance). 24 | * @see {@link com.inductiveautomation.xopc.driver.api.DriverContext#getDeviceName DriverContext.getDeviceName} 25 | */ 26 | public String getDeviceName(); 27 | 28 | /** 29 | * @return 30 | * The base name to be used when creating sub-loggers. 31 | */ 32 | public String getLoggerName(); 33 | 34 | /** 35 | * Returns the disk path for this driver instance. The path contains the unique driver id. 36 | * 37 | * @return 38 | * The disk path to store message queues. 39 | */ 40 | public String getDiskPath(); 41 | 42 | /** 43 | * @return 44 | * A new {@link VariableNodeBuilder} 45 | */ 46 | public UaVariableNodeBuilder getVariableNodeBuilder(); 47 | 48 | /** 49 | * @return 50 | * A new {@link VariableNodeBuilder} 51 | */ 52 | public UaObjectNodeBuilder getObjectNodeBuilder(); 53 | 54 | /** 55 | * @return 56 | * The {@link UaNodeContext} 57 | */ 58 | public UaNodeContext getNodeContext(); 59 | 60 | /** 61 | * Adds the node to the NodeManager by calling {@link #UaNodeManager.addNode(node)} 62 | * and adds the given address to the drivers browse tree. 63 | * 64 | * @param node 65 | * A UaNode obtained from {@link #getVariableNodeBuilder()} or {@link #getObjectNodeBuilder()} 66 | * @param address 67 | * The tag address to register for browsing. 68 | * @return 69 | * The added node 70 | */ 71 | public UaNode addNode(UaNode node, String address); 72 | 73 | /** 74 | * Remove a Node from the drivers NodeManager. 75 | * 76 | * @param node 77 | * The node to remove. 78 | * @see org.eclipse.milo.opcua.sdk.server.UaNodeManager#removeNode(UaNode) 79 | */ 80 | public void removeNode(UaNode node); 81 | 82 | /** 83 | * Execute the given command in the drivers ExecutionManager. 84 | * 85 | * @param command 86 | * The Runnable to execute. 87 | * @see com.inductiveautomation.ignition.common.execution.ExecutionManager#executeOnce(Runnable) ExecutionManager.executeOnce(Runnable) 88 | */ 89 | public void executeOnce(Runnable command); 90 | 91 | /** 92 | * Schedule the given command in the drivers ExecutionManager. 93 | * 94 | * @param command 95 | * The Runnable to execute. 96 | * 97 | * @see com.inductiveautomation.ignition.common.execution.ExecutionManager#executeOnce(Runnable, long, TimeUnit) ExecutionManager.executeOnce(Runnable, long, TimeUnit) 98 | */ 99 | public ScheduledFuture executeOnce(java.lang.Runnable command, long delay, TimeUnit unit); 100 | 101 | /** 102 | * Registers a self scheduling command to be executed. Self scheduling commands provide their own execution delay. 103 | * 104 | * @param owner 105 | * Name of the "owner"- just a string qualifier for the command name. 106 | * @param name 107 | * Identifier used in conjunction with the owner to identify the command. 108 | * @param command 109 | * The Runnable to execute. 110 | * 111 | * @see com.inductiveautomation.ignition.common.execution.ExecutionManager#register(String, String, SelfSchedulingRunnable) ExecutionManager.register(String, String, SelfSchedulingRunnable) 112 | */ 113 | void registerSelfSchedulingRunnable(String owner, String name, SelfSchedulingRunnable command); 114 | 115 | /** 116 | * Unregisters a given command. If it is in the process of executing, it will be interrupted. 117 | * 118 | * @param owner 119 | * Name of the "owner"- just a string qualifier for the command name. 120 | * @param name 121 | * Identifier used in conjunction with the owner to identify the command. 122 | * 123 | * @see com.inductiveautomation.ignition.common.execution.ExecutionManager#unregister(String, String) ExecutionManager.unregister(String, String) 124 | */ 125 | void unregisterScheduledRunnable(String owner, String name); 126 | 127 | /** 128 | * Writes a message to the connected remote device. 129 | * 130 | * @param message 131 | * The message to write. 132 | * @param deviceId 133 | * The remote device to write to 134 | */ 135 | public void writeToRemoteDevice(ByteBuffer message, int deviceId); 136 | 137 | /** 138 | * @return 139 | * The RedundancyManager 140 | */ 141 | //public RedundancyManager getRedundancyManager(); 142 | 143 | /** 144 | * @return 145 | * true if this is the active node in a redundant configuration (or if redundancy is disabled). 146 | */ 147 | public boolean isActiveNode(); 148 | 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/ModuleHook.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2015 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp; 17 | 18 | import java.io.IOException; 19 | import java.net.MalformedURLException; 20 | import java.net.URL; 21 | import java.net.URLConnection; 22 | import java.util.List; 23 | import java.util.ResourceBundle; 24 | 25 | import org.apache.wicket.RuntimeConfigurationType; 26 | 27 | import com.chitek.ignition.drivers.generictcp.configuration.GenericTcpServerDriverType; 28 | import com.chitek.ignition.drivers.generictcp.configuration.GenericTcpClientDriverType; 29 | import com.chitek.ignition.drivers.generictcp.configuration.settings.GenericTcpServerDriverSettings; 30 | import com.chitek.ignition.drivers.generictcp.configuration.settings.GenericTcpClientDriverSettings; 31 | import com.google.common.collect.Lists; 32 | import com.inductiveautomation.ignition.common.BundleUtil; 33 | import com.inductiveautomation.ignition.gateway.model.GatewayContext; 34 | import com.inductiveautomation.xopc.driver.api.configuration.DriverType; 35 | import com.inductiveautomation.xopc.driver.common.AbstractDriverModuleHook; 36 | 37 | public class ModuleHook extends AbstractDriverModuleHook { 38 | 39 | public static final String BUNDLE_PREFIX = "GenericTcpDriver"; 40 | private static final int expectedApiVersion = 4; 41 | 42 | private static final List DRIVER_TYPES = Lists.newArrayList(); 43 | static { 44 | DRIVER_TYPES.add(new GenericTcpClientDriverType()); 45 | DRIVER_TYPES.add(new GenericTcpServerDriverType()); 46 | } 47 | 48 | @Override 49 | public void setup(GatewayContext context) { 50 | // Register class with BundleUtil for localization 51 | BundleUtil.get().addBundle(BUNDLE_PREFIX, ModuleHook.class, "GenericTcpDriver"); 52 | 53 | // Disable caching in development mode - Without this, the use of bundle util 54 | // keeps the jar file opened, even after a driver is reloaded by the dev module. 55 | // Not a big problem, as the temp jar files won't be deleted until the JVM shuts down, 56 | // but with this option the files can be deleted manually, if all classes are properly 57 | // unloaded. This allows a quick test for memory leaks. 58 | if (context.getWebResourceManager().getWebApplication().getConfigurationType() == RuntimeConfigurationType.DEVELOPMENT) { 59 | context.getWebResourceManager().getWebApplication().getResourceSettings().getLocalizer().clearCache(); 60 | URLConnection con; 61 | try { 62 | con = new URLConnection(new URL("file://null")) { 63 | 64 | @Override 65 | public void connect() throws IOException { 66 | // NOOP - This is just a dummy 67 | 68 | } 69 | }; 70 | // This will affect all URLConnections - not sure about side effects 71 | con.setDefaultUseCaches(false); 72 | } catch (MalformedURLException e) { 73 | e.printStackTrace(); 74 | } 75 | } 76 | 77 | // Clear Wicket's markup cache. Actually this is only necessary when the module is upgraded to a new version, but there's 78 | // no way to detect this situation. 79 | if (context.getWebResourceManager().getWebApplication().getMarkupSettings().getMarkupFactory().hasMarkupCache()) { 80 | // When the gateway service is started, the is no markup cache yet. 81 | context.getWebResourceManager().getWebApplication().getResourceSettings().getPropertiesFactory().clearCache(); 82 | context.getWebResourceManager().getWebApplication().getMarkupSettings().getMarkupFactory().getMarkupCache().clear(); 83 | } 84 | 85 | super.setup(context); 86 | } 87 | 88 | @Override 89 | public void shutdown() { 90 | // Remove localization 91 | BundleUtil.get().removeBundle(BUNDLE_PREFIX); 92 | 93 | // These Bundles are registered automatically by the API but never removed, so we have to do it here 94 | BundleUtil.get().removeBundle(GenericTcpServerDriverSettings.class.getSimpleName()); 95 | BundleUtil.get().removeBundle(GenericTcpClientDriverSettings.class.getSimpleName()); 96 | ResourceBundle.clearCache(); 97 | 98 | super.shutdown(); 99 | } 100 | 101 | @Override 102 | protected int getExpectedAPIVersion() { 103 | return expectedApiVersion; 104 | } 105 | 106 | @Override 107 | protected List getDriverTypes() { 108 | return DRIVER_TYPES; 109 | } 110 | 111 | public boolean isFreeModule() { 112 | return true; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/configuration/GenericTcpClientDriverType.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.configuration; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.Locale; 6 | 7 | import com.chitek.ignition.drivers.generictcp.GenericTcpClientDriver; 8 | import com.chitek.ignition.drivers.generictcp.configuration.settings.GenericTcpClientDriverSettings; 9 | import com.chitek.ignition.drivers.generictcp.meta.config.ui.ConfigUiTabs; 10 | import com.inductiveautomation.ignition.common.BundleUtil; 11 | import com.inductiveautomation.ignition.gateway.localdb.persistence.PersistentRecord; 12 | import com.inductiveautomation.ignition.gateway.localdb.persistence.RecordMeta; 13 | import com.inductiveautomation.ignition.gateway.localdb.persistence.ReferenceField; 14 | import com.inductiveautomation.ignition.gateway.web.components.ConfigPanel; 15 | import com.inductiveautomation.ignition.gateway.web.pages.IConfigPage; 16 | import com.inductiveautomation.xopc.driver.api.Driver; 17 | import com.inductiveautomation.xopc.driver.api.DriverContext; 18 | import com.inductiveautomation.ignition.gateway.opcua.server.api.DeviceSettingsRecord; 19 | import com.inductiveautomation.xopc.driver.api.configuration.DriverType; 20 | import com.inductiveautomation.xopc.driver.api.configuration.links.ConfigurationUILink; 21 | import com.inductiveautomation.xopc.driver.api.configuration.links.LinkEntry; 22 | 23 | @SuppressWarnings("serial") 24 | public class GenericTcpClientDriverType extends DriverType { 25 | 26 | public static final String TYPE_ID = "GenericTcpClient"; 27 | 28 | public GenericTcpClientDriverType() { 29 | super(TYPE_ID, "GenericTcpDriver.GenericTcpClientDriverType.Name", "GenericTcpDriver.GenericTcpClientDriverType.Description"); 30 | } 31 | 32 | @Override 33 | public Driver createDriver(DriverContext driverContext, DeviceSettingsRecord deviceSettings) { 34 | GenericTcpClientDriverSettings settings = findProfileSettingsRecord(driverContext.getGatewayContext(), deviceSettings); 35 | 36 | return new GenericTcpClientDriver(driverContext, settings); 37 | } 38 | 39 | @Override 40 | public RecordMeta getSettingsRecordType() { 41 | return GenericTcpClientDriverSettings.META; 42 | } 43 | 44 | @Override 45 | public ReferenceField getSettingsRecordForeignKey() { 46 | return GenericTcpClientDriverSettings.DeviceSettings; 47 | } 48 | 49 | @Override 50 | public List getLinks() { 51 | LinkEntry[] list = {new ConfigLink()}; 52 | return Arrays.asList(list); 53 | } 54 | 55 | /* 56 | * Link to PacketConfigUI 57 | */ 58 | private static class ConfigLink implements ConfigurationUILink { 59 | 60 | @Override 61 | public String getLinkText(Locale locale) { 62 | return BundleUtil.get().getStringLenient(locale, "GenericTcpDriver.configLink"); 63 | } 64 | 65 | @Override 66 | public ConfigPanel getConfigurationUI(IConfigPage configPage, ConfigPanel returnPanel, PersistentRecord record, Callback callback) { 67 | return new ConfigUiTabs(configPage, returnPanel, record, callback); 68 | } 69 | 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/configuration/GenericTcpServerDriverType.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.configuration; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.Locale; 6 | 7 | import com.chitek.ignition.drivers.generictcp.GenericTcpServerDriver; 8 | import com.chitek.ignition.drivers.generictcp.configuration.settings.GenericTcpServerDriverSettings; 9 | import com.chitek.ignition.drivers.generictcp.meta.config.ui.ConfigUiTabs; 10 | import com.inductiveautomation.ignition.common.BundleUtil; 11 | import com.inductiveautomation.ignition.gateway.localdb.persistence.PersistentRecord; 12 | import com.inductiveautomation.ignition.gateway.localdb.persistence.RecordMeta; 13 | import com.inductiveautomation.ignition.gateway.localdb.persistence.ReferenceField; 14 | import com.inductiveautomation.ignition.gateway.web.components.ConfigPanel; 15 | import com.inductiveautomation.ignition.gateway.web.pages.IConfigPage; 16 | import com.inductiveautomation.xopc.driver.api.Driver; 17 | import com.inductiveautomation.xopc.driver.api.DriverContext; 18 | import com.inductiveautomation.ignition.gateway.opcua.server.api.DeviceSettingsRecord; 19 | import com.inductiveautomation.xopc.driver.api.configuration.DriverType; 20 | import com.inductiveautomation.xopc.driver.api.configuration.links.ConfigurationUILink; 21 | import com.inductiveautomation.xopc.driver.api.configuration.links.LinkEntry; 22 | 23 | @SuppressWarnings("serial") 24 | public class GenericTcpServerDriverType extends DriverType { 25 | 26 | public static final String TYPE_ID = "GenericTcpServer"; 27 | 28 | public GenericTcpServerDriverType() { 29 | super(TYPE_ID, "GenericTcpDriver.GenericTcpServerDriverType.Name", "GenericTcpDriver.GenericTcpServerDriverType.Description"); 30 | } 31 | 32 | @Override 33 | public Driver createDriver(DriverContext driverContext, DeviceSettingsRecord deviceSettings) { 34 | GenericTcpServerDriverSettings settings = findProfileSettingsRecord(driverContext.getGatewayContext(), deviceSettings); 35 | 36 | return new GenericTcpServerDriver(driverContext, settings); 37 | } 38 | 39 | @Override 40 | public RecordMeta getSettingsRecordType() { 41 | return GenericTcpServerDriverSettings.META; 42 | } 43 | 44 | @Override 45 | public ReferenceField getSettingsRecordForeignKey() { 46 | return GenericTcpServerDriverSettings.DeviceSettings; 47 | } 48 | 49 | @Override 50 | public List getLinks() { 51 | LinkEntry[] list = {new ConfigLink()}; 52 | return Arrays.asList(list); 53 | } 54 | 55 | /* 56 | * Link to PacketConfigUI 57 | */ 58 | private static class ConfigLink implements ConfigurationUILink { 59 | 60 | @Override 61 | public String getLinkText(Locale locale) { 62 | return BundleUtil.get().getStringLenient(locale, "GenericTcpDriver.configLink"); 63 | } 64 | 65 | @Override 66 | public ConfigPanel getConfigurationUI(IConfigPage configPage, ConfigPanel returnPanel, PersistentRecord record, Callback callback) { 67 | return new ConfigUiTabs(configPage, returnPanel, record, callback); 68 | } 69 | 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/configuration/settings/GenericTcpClientDriverSettings.properties: -------------------------------------------------------------------------------- 1 | # Categories 2 | Category.Connectivity=Connectivity 3 | Category.MessageHandling=Message Handling 4 | 5 | # Properties 6 | Hostname.Name=Hostname 7 | Hostname.Desc=Hostname/IP address of the device to connect to. 8 | 9 | Port.Name=Port 10 | Port.Desc=Port to connect to. 11 | 12 | ConnectOnStartup.Name=Connect on startup 13 | ConnectOnStartup.Desc=If false, the driver will not connect on startup, but waits for an OPC client to set the '[Status]/Connect' tag to true. 14 | 15 | PacketTimeout.Name=Packet Timeout 16 | PacketTimeout.Desc=Maximum time (in milliseconds) between incoming data packets of one message. 17 | 18 | ReverseByteOrder.Name=Reverse byte order 19 | ReverseByteOrder.Desc=Set true to use LittleEndian (Intel) byte order instead of default BigEndian (Motorola). 20 | 21 | Timeout.Name=Receive Timeout 22 | Timeout.Desc=Amount of time (in milliseconds) without receiving data from the device before a disconnect/reconnect is made. Set to 0 to disable. 23 | 24 | TimestampFactor.Name=Timestamp factor 25 | TimestampFactor.Desc=A multiplier for the timestamp / message age received from the device. The driver expects timestamps in milliseconds. 26 | 27 | MaxTimestamp.Name=Max. Timestamp 28 | MaxTimestamp.Desc=The maximum possible timestamp value sent by the device. This is used to detect an overflow. -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/configuration/settings/GenericTcpClientDriverSettings_de.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/main/java/com/chitek/ignition/drivers/generictcp/configuration/settings/GenericTcpClientDriverSettings_de.properties -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/configuration/settings/GenericTcpServerDriverSettings.properties: -------------------------------------------------------------------------------- 1 | # Categories 2 | Category.Connectivity=Connectivity 3 | Category.MessageHandling=Message Handling 4 | 5 | # Properties 6 | ServerHostname.Name=Local endpoint address 7 | ServerHostname.Desc=The local ip address that is used by the driver. Optional, if empty, the address of the default network interface is used. 8 | 9 | ServerPort.Name=Server Port 10 | ServerPort.Desc=Local port to listen on. 11 | 12 | Timeout.Name=Connection Timeout 13 | Timeout.Desc=When no data is received from a device for this amount of time (in seconds), the connection is closed by the driver. The client has to reconnect in this case. Set to 0 to disable. 14 | 15 | UseUdp.Name=UDP mode 16 | UseUdp.Desc=Use UDP connections instead of TCP. 17 | 18 | AcceptAll.Name=Accept all connections 19 | AcceptAll.Desc=Ignore the 'Devices' list and accept all incoming connections. 20 | 21 | Devices.Name=Devices 22 | Devices.Desc=List of accepted devices. Use one line with 'hostname,alias' line for each device.
Beispiel:
10.224.1.99,Device1
device.com,Device2
23 | 24 | PacketTimeout.Name=Packet Timeout 25 | PacketTimeout.Desc=Maximum time (in milliseconds) between incoming data packets of one message. 26 | 27 | TimestampFactor.Name=Timestamp factor 28 | TimestampFactor.Desc=A multiplier for the timestamp / message age received from the device. The driver expects timestamps in milliseconds. 29 | 30 | MaxTimestamp.Name=Max. Timestamp 31 | MaxTimestamp.Desc=The maximum possible timestamp value sent by the device. This is used to detect an overflow. 32 | 33 | ReverseByteOrder.Name=Reverse byte order 34 | ReverseByteOrder.Desc=Set true to use LittleEndian (Intel) byte order instead of default BigEndian (Motorola). -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/configuration/settings/GenericTcpServerDriverSettings_de.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/main/java/com/chitek/ignition/drivers/generictcp/configuration/settings/GenericTcpServerDriverSettings_de.properties -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/configuration/settings/IGenericTcpDriverSettings.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.configuration.settings; 2 | 3 | import java.io.Serializable; 4 | 5 | import com.chitek.ignition.drivers.generictcp.meta.config.DriverConfig; 6 | import com.chitek.ignition.drivers.generictcp.meta.config.HeaderConfig; 7 | import com.chitek.ignition.drivers.generictcp.meta.config.WritebackConfig; 8 | import com.inductiveautomation.ignition.gateway.localdb.persistence.PersistentRecord; 9 | 10 | public interface IGenericTcpDriverSettings extends Serializable { 11 | 12 | /** 13 | * @return 14 | * This class, cast to a PersistentRecord. 15 | */ 16 | public PersistentRecord getPersistentRecord(); 17 | 18 | public byte[] getMessageConfig(); 19 | 20 | public DriverConfig getParsedMessageConfig() throws Exception; 21 | 22 | public byte[] getHeaderConfig(); 23 | 24 | public HeaderConfig getParsedHeaderConfig() throws Exception; 25 | 26 | public byte[] getWritebackConfig(); 27 | 28 | public WritebackConfig getParsedWritebackConfig() throws Exception; 29 | 30 | public void setMessageConfig(byte[] messageConfig); 31 | 32 | public void setHeaderConfig(byte[] headerConfig); 33 | 34 | public void setWritebackConfig(byte[] writebackConfig); 35 | 36 | /** 37 | * @return 38 | * true if writeback config page should be shown 39 | */ 40 | public boolean isWritebackEnabled(); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/folder/BrowseTree.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.folder; 2 | 3 | import com.inductiveautomation.xopc.driver.util.TagTree; 4 | 5 | public class BrowseTree extends TagTree { 6 | 7 | /** 8 | * Called by the message folders to add tags to the browse tree. Folder nodes are created when neccessary. 9 | * The TagTree is a litte bit abused here. Normally addresses are seperated by '/' but i want arrays 10 | * to appear as branches of the tree, so arrays are forced to be seperate by replacing the array indicator. 11 | * The Tag element of the TreeNode contains the real, unmodified address of the node. 12 | * 13 | * @param tag 14 | */ 15 | public TagTreeNode addTag(String address) { 16 | String treeAddress = address.replaceAll("\\[(\\d+|(?:raw))\\]", "/[$1]"); 17 | return super.addTag(treeAddress, address); 18 | } 19 | 20 | /** 21 | * Returns the TreeNode with the given address. Array elements '[.]' are treated as subelements of the tree 22 | */ 23 | @Override 24 | public TagTreeNode findTag(String address) { 25 | String treeAddress = address.replaceAll("\\[(\\d+|(?:raw))\\]", "/[$1]"); 26 | return super.findTag(treeAddress); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/folder/DeviceStatusFolder.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2019 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.folder; 17 | 18 | import org.eclipse.milo.opcua.stack.core.BuiltinDataType; 19 | import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue; 20 | import org.eclipse.milo.opcua.stack.core.types.builtin.Variant; 21 | 22 | import com.chitek.ignition.drivers.generictcp.IGenericTcpDriverContext; 23 | import com.inductiveautomation.xopc.driver.api.tags.DynamicDriverTag; 24 | 25 | /** 26 | * This tag folder contains the status tags for a passive device 27 | * 28 | */ 29 | public class DeviceStatusFolder extends MessageFolder { 30 | 31 | public static final String FOLDER_NAME = "[Status]"; 32 | 33 | private DataValue isConnectedValue; 34 | 35 | private boolean isConnected = false; 36 | 37 | public DeviceStatusFolder(IGenericTcpDriverContext driverContext, int deviceId, String deviceAlias) { 38 | super(driverContext, FolderManager.getFolderId(deviceId, FolderManager.DEVICE_STATUS_ID), deviceAlias); 39 | 40 | isConnectedValue = new DataValue(new Variant(false)); 41 | 42 | addSpecialTags(deviceAlias); 43 | } 44 | 45 | @Override 46 | public void connectionStateChanged(boolean isConnected) { 47 | if (this.isConnected != isConnected) { 48 | isConnectedValue = new DataValue(new Variant(isConnected)); 49 | this.isConnected = isConnected; 50 | } 51 | } 52 | 53 | @Override 54 | public void activityLevelChanged(boolean isActive) { 55 | // This folder ignores the activity level 56 | } 57 | 58 | /** 59 | * Adds the special tags in this message to the NodeManager and the Drivers browseTree. 60 | * 61 | * @param folderName 62 | * The name of the tag folder 63 | */ 64 | private void addSpecialTags(String deviceAlias) { 65 | 66 | // Create the device root folder 67 | buildAndAddFolderNode(deviceAlias, deviceAlias); 68 | 69 | // Create the folder node 70 | String folderName = String.format("%s/%s", deviceAlias, FOLDER_NAME); 71 | buildAndAddFolderNode(folderName, FOLDER_NAME); 72 | 73 | // Special tags 74 | DynamicDriverTag driverTag = new DynamicDriverTag(folderName + "/Is Connected", BuiltinDataType.Boolean) { 75 | @Override 76 | public DataValue getValue() { 77 | return isConnectedValue; 78 | } 79 | }; 80 | buildAndAddNode(driverTag).setValue(new DataValue(new Variant(false))); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/folder/ISubscriptionChangeListener.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.folder; 2 | 3 | import java.util.Set; 4 | 5 | public interface ISubscriptionChangeListener { 6 | 7 | /** 8 | * Called by the SubscriptionUpdater after items have been added or removed. 9 | * 10 | * @param rate 11 | * The subscription rate in milliseconds 12 | * @param itemAdresses 13 | * The addresses of all subscribed items 14 | */ 15 | public void subscriptionChanged(long rate, Set itemAdresses); 16 | 17 | /** 18 | * Called by the subscription updater before subscribed items are updated 19 | */ 20 | public void beforeSubscriptionUpdate(); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/folder/MessageDataWrapper.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.folder; 2 | 3 | import java.nio.ByteOrder; 4 | 5 | import com.chitek.ignition.drivers.generictcp.util.VariantByteBuffer; 6 | import com.inductiveautomation.xopc.driver.util.ByteUtilities; 7 | 8 | /** 9 | * A wrapper class for the byte array with message data 10 | */ 11 | public class MessageDataWrapper { 12 | 13 | public static byte[] wrapMessage(long receiveTimestamp, long headerTimestamp, short sequenceId, byte[] payload, ByteOrder byteOrder) { 14 | byte[] messageData = new byte[16 + payload.length]; 15 | 16 | // Copy the timestamp to the first 8 byte of the message 17 | // The timestamp is shifted left by 2 Bytes, the message number is then added 18 | byte[] timestamp = ByteUtilities.get(byteOrder).fromLong( (receiveTimestamp << 16) + sequenceId); 19 | System.arraycopy(timestamp, 0, messageData, 0, 8); 20 | 21 | // Copy the header timestamp to the second 8 byte of the message 22 | timestamp = ByteUtilities.get(byteOrder).fromLong(headerTimestamp); 23 | System.arraycopy(timestamp, 0, messageData, 8, 8); 24 | 25 | // Copy the payload data 26 | System.arraycopy(payload, 0, messageData, 16, payload.length); 27 | 28 | return messageData; 29 | } 30 | 31 | private int sequenceId; 32 | private long timeReceived; 33 | private long headerTimestamp; 34 | 35 | /** 36 | * Evaluate the message info form the given buffer. After execution, the buffer is positioned at the begin of the 37 | * message payload. 38 | * 39 | * @param messageData 40 | * @return 41 | * The length in bytes of the message payload data. 42 | */ 43 | public int evaluateData(VariantByteBuffer messageData) { 44 | 45 | // Read the timestamps from the message 46 | long id = messageData.getLong(); 47 | sequenceId = (int) (id & (0xffff)); 48 | long timestamp = id >> 16; // Remove the sequence number 49 | timeReceived = timestamp; 50 | 51 | headerTimestamp = messageData.getLong(); 52 | 53 | return messageData.remaining(); 54 | } 55 | 56 | /** 57 | * @return 58 | * The sequence id if multiple messages where received in the same package with the same timestamp. 59 | */ 60 | public int getSequenceId() { 61 | return sequenceId; 62 | } 63 | 64 | /** 65 | * @return 66 | * Time when this message was received by the driver 67 | */ 68 | public long getTimeReceived() { 69 | return timeReceived; 70 | } 71 | 72 | /** 73 | * @return 74 | * Timestamp received with the message header 75 | */ 76 | public long getHeaderTimestamp() { 77 | return headerTimestamp; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/io/ClientEventHandler.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.io; 2 | 3 | import java.io.IOException; 4 | import java.net.SocketTimeoutException; 5 | import java.nio.ByteBuffer; 6 | 7 | import org.apache.log4j.Logger; 8 | 9 | import com.chitek.ignition.drivers.generictcp.folder.MessageHeader; 10 | import com.chitek.ignition.drivers.generictcp.meta.config.DriverConfig; 11 | import com.chitek.ignition.drivers.generictcp.meta.config.IDriverSettings; 12 | import com.inductiveautomation.ignition.common.execution.ExecutionManager; 13 | import com.inductiveautomation.iosession.async.IOEventHandler; 14 | import com.inductiveautomation.iosession.socket.IOTimeoutHandler; 15 | 16 | public class ClientEventHandler implements IOEventHandler, IOTimeoutHandler { 17 | 18 | private final IMessageHandler messageHandler; 19 | private final MessageState state; 20 | 21 | public ClientEventHandler(Logger log, ExecutionManager executionManager, DriverConfig driverConfig, IDriverSettings driverSettings, MessageHeader messageHeader, IMessageHandler messageHandler) { 22 | this.messageHandler = messageHandler; 23 | 24 | state = new MessageState(null, executionManager, messageHeader, driverConfig, driverSettings, log); 25 | state.setMessageHandler(messageHandler); 26 | } 27 | 28 | 29 | @Override 30 | public void dataArrived(byte[] data, int bytesRead) { 31 | state.addData(ByteBuffer.wrap(data, 0, bytesRead)); 32 | } 33 | 34 | @Override 35 | public void connectionLost(IOException e) { 36 | messageHandler.clientDisconnected(null); 37 | } 38 | 39 | @Override 40 | public void readTimeout(final SocketTimeoutException e) { 41 | messageHandler.clientReadTimeout(null); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/io/IIoEventHandler.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.io; 17 | 18 | import java.net.InetSocketAddress; 19 | import java.nio.ByteBuffer; 20 | 21 | public interface IIoEventHandler { 22 | 23 | /** 24 | * Called when a remote client connects. 25 | * 26 | * @param remoteSocket 27 | * @return 28 | * true to accept the connection. 29 | */ 30 | public abstract boolean clientConnected(InetSocketAddress remoteSocket); 31 | 32 | /** 33 | * Called when a remote client closes the connection. 34 | * 35 | * @param remoteAddress 36 | */ 37 | public abstract void connectionLost(InetSocketAddress remoteAddress); 38 | 39 | public abstract void dataArrived(InetSocketAddress remoteAddress, ByteBuffer data, int bytesRead); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/io/IMessageHandler.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.io; 2 | 3 | import java.net.InetSocketAddress; 4 | 5 | public interface IMessageHandler { 6 | 7 | /** 8 | * @param remoteSocket 9 | * @param messageId 10 | * @param messageData 11 | * The received message
12 | * 0..7 - Packet receive timestamp
13 | * 8..15 - Header Timestamp
14 | * 16... - The message data 15 | * @param handshakeMessage 16 | */ 17 | public void messageReceived(InetSocketAddress remoteSocket, int messageId, byte[] messageData, byte[] handshakeMessage); 18 | 19 | public boolean clientConnected(InetSocketAddress remoteSocket); 20 | 21 | public void clientDisconnected(InetSocketAddress remoteSocket); 22 | 23 | /** 24 | * A read timeout occurred. This method is only used for client sockets. 25 | * 26 | * @param remoteSocket 27 | */ 28 | public void clientReadTimeout(InetSocketAddress remoteSocket); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/io/NioEventHandler.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.io; 17 | 18 | import java.net.InetSocketAddress; 19 | import java.nio.ByteBuffer; 20 | import java.util.Arrays; 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | 24 | import org.apache.log4j.Logger; 25 | 26 | import com.chitek.ignition.drivers.generictcp.folder.MessageHeader; 27 | import com.chitek.ignition.drivers.generictcp.meta.config.DriverConfig; 28 | import com.chitek.ignition.drivers.generictcp.meta.config.IDriverSettings; 29 | import com.inductiveautomation.ignition.common.execution.ExecutionManager; 30 | import com.inductiveautomation.xopc.driver.util.ByteUtilities; 31 | 32 | public class NioEventHandler implements IIoEventHandler { 33 | 34 | private final Logger log; 35 | 36 | private final ExecutionManager executionManager; 37 | private final DriverConfig driverConfig; 38 | private final IDriverSettings driverSettings; 39 | private final MessageHeader messageHeader; 40 | private final IMessageHandler messageHandler; 41 | 42 | private final Map clientMap=new HashMap(); 43 | 44 | public NioEventHandler(Logger log, ExecutionManager executionManager, DriverConfig driverConfig, IDriverSettings driverSettings, MessageHeader messageHeader, IMessageHandler messageHandler) { 45 | this.log = log; 46 | this.executionManager = executionManager; 47 | this.driverConfig = driverConfig; 48 | this.driverSettings = driverSettings; 49 | this.messageHeader = messageHeader; 50 | this.messageHandler = messageHandler; 51 | } 52 | 53 | @Override 54 | public boolean clientConnected(InetSocketAddress remoteSocket) { 55 | return messageHandler.clientConnected(remoteSocket); 56 | } 57 | 58 | @Override 59 | public void connectionLost(InetSocketAddress remoteSocket) { 60 | clientMap.remove(remoteSocket); 61 | messageHandler.clientDisconnected(remoteSocket); 62 | } 63 | 64 | @Override 65 | public void dataArrived(InetSocketAddress remoteSocket, ByteBuffer data, int bytesRead) { 66 | if (log.isTraceEnabled()) { 67 | log.trace(String.format("Received %d bytes from %s: %s", data.remaining(), remoteSocket.toString(), ByteUtilities.toString(Arrays.copyOfRange(data.array(), 0, bytesRead)))); 68 | } 69 | 70 | MessageState state = getMessageState(remoteSocket); 71 | 72 | state.addData(data); 73 | } 74 | 75 | /** 76 | * Return the message state for the given remote address. 77 | * @param remoteAddress 78 | * @return 79 | */ 80 | private MessageState getMessageState(InetSocketAddress remoteSocket) { 81 | MessageState state = clientMap.get(remoteSocket); 82 | if (state == null) { 83 | state = new MessageState(remoteSocket, executionManager, messageHeader, driverConfig, driverSettings, log); 84 | state.setMessageHandler(messageHandler); 85 | clientMap.put(remoteSocket, state); 86 | } 87 | 88 | return state; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/io/NioServer.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.io; 2 | 3 | import java.net.InetSocketAddress; 4 | import java.nio.ByteBuffer; 5 | 6 | public interface NioServer { 7 | 8 | public void start(); 9 | 10 | public void stop(); 11 | 12 | public void setEventHandler(IIoEventHandler eventHandler); 13 | 14 | public void setTimeout(long timeout); 15 | 16 | public void write(InetSocketAddress remoteSocketAddress, ByteBuffer data); 17 | 18 | public int getConnectedClientCount(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/io/TimeoutHandler.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2016 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.io; 17 | 18 | import java.net.SocketAddress; 19 | import java.util.HashMap; 20 | import java.util.Iterator; 21 | import java.util.Map; 22 | import java.util.Map.Entry; 23 | 24 | /** 25 | * A helper class for handling timeouts with an NIO server socket. 26 | * 27 | */ 28 | public class TimeoutHandler { 29 | /** The timeout in milliseconds **/ 30 | private long timeout=0; 31 | 32 | private long oldestTime=0; 33 | private SocketAddress oldestAddress = null; 34 | private Map timeoutMap = new HashMap(); 35 | 36 | /** 37 | * @param timeout 38 | * The timeout in milliseconds. A value of 0 disables the timeout. 39 | */ 40 | public TimeoutHandler(long timeout) { 41 | this.timeout=timeout; 42 | } 43 | 44 | /** 45 | * Reset the timeout for the given address (when data is received from that address). 46 | * 47 | * @param address 48 | * The InetAddress for which to reset the timeout 49 | */ 50 | public void dataReceived(SocketAddress address) { 51 | 52 | timeoutMap.put(address, System.nanoTime()/1000000); 53 | 54 | if (address.equals(oldestAddress) || oldestAddress==null) { 55 | // We need to update the oldest client 56 | updateTimeout(); 57 | } 58 | } 59 | 60 | /** 61 | * Remove the given address from the internal map (when the connection to that address is closed). 62 | * 63 | * @param address 64 | * The InetAddress to remove 65 | */ 66 | public void removeAddress(SocketAddress address) { 67 | timeoutMap.remove(address); 68 | if (timeoutMap.isEmpty()) { 69 | oldestAddress = null; 70 | return; 71 | } 72 | 73 | if (address.equals(oldestAddress)) { 74 | // We need to update the oldest client 75 | updateTimeout(); 76 | } 77 | } 78 | 79 | /** 80 | * Returns the time to the next timeout. If the time is already expired, this method returns 1 instead of 0, because 81 | * 0 would disable the timeout when calling blocking socket methods. 82 | * 83 | * @return 84 | * Milliseconds until the next timeout, or 0 if the timeout is disabled. 85 | */ 86 | public long getTimeToTimeout() { 87 | if (oldestAddress==null || timeout==0) { 88 | return timeout; 89 | } 90 | 91 | long diff = System.nanoTime()/1000000-oldestTime; 92 | return Math.max(1, timeout-diff); 93 | } 94 | 95 | /** 96 | * Test if the timeout is expired 97 | * 98 | * @return 99 | * true if the timeout is expired 100 | */ 101 | public boolean isTimeoutExpired() { 102 | if (oldestAddress==null || timeout==0) { 103 | return false; 104 | } 105 | 106 | long diff = System.nanoTime()/1000000-oldestTime; 107 | return (timeout-diff)<=0; 108 | } 109 | 110 | /** 111 | * Return the InetAddress whose timeout is about to or has expired. 112 | * 113 | * @return 114 | * The InetAddress that caused a timeout. 115 | */ 116 | public SocketAddress getTimeoutAddress() { 117 | return oldestAddress; 118 | } 119 | 120 | private void updateTimeout() { 121 | long earliestTime=Long.MAX_VALUE; 122 | SocketAddress address=null; 123 | Iterator> it = timeoutMap.entrySet().iterator(); 124 | while (it.hasNext()) { 125 | Entryentry = it.next(); 126 | if (entry.getValue() < earliestTime) { 127 | earliestTime=entry.getValue(); 128 | address=entry.getKey(); 129 | } 130 | } 131 | 132 | oldestTime=earliestTime; 133 | oldestAddress=address; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/DriverConfig.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.meta.config; 17 | 18 | import java.io.Serializable; 19 | import java.util.ArrayList; 20 | import java.util.Collections; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | import com.chitek.ignition.drivers.generictcp.types.OptionalDataType; 26 | import com.chitek.util.XMLConfigParser; 27 | 28 | public class DriverConfig implements Serializable { 29 | 30 | private static final long serialVersionUID = 1L; 31 | private static final String XML_CONFIG_NAME = "DriverConfig"; 32 | 33 | private OptionalDataType messageIdType = OptionalDataType.UByte; 34 | private String parserWarnings = null; 35 | 36 | public Map messages = new HashMap(); 37 | 38 | /** 39 | * Returns the message with the given ID. 40 | * 41 | * @param messageId 42 | * @return 43 | * The message configuration with the given Id, or null if message is not defined 44 | */ 45 | public MessageConfig getMessageConfig(int messageId) { 46 | return messages.get(messageId); 47 | } 48 | 49 | /** 50 | * @return 51 | * A sorted list (by id) of the configured messages. 52 | */ 53 | public List getMessageList() { 54 | List list = new ArrayList(messages.values()); 55 | Collections.sort(list); 56 | return list; 57 | } 58 | 59 | public void setMessageIdType(OptionalDataType messageIdByteSize) { 60 | this.messageIdType = messageIdByteSize; 61 | } 62 | 63 | public OptionalDataType getMessageIdType() { 64 | return messageIdType; 65 | } 66 | 67 | /** 68 | * Method used by XML-Parser 69 | * @param messageLengthName 70 | */ 71 | public void setMessageIdType(String messageIdTypeName) { 72 | this.messageIdType = OptionalDataType.valueOf(messageIdTypeName); 73 | } 74 | 75 | public void addMessageConfig(MessageConfig messageConfig) { 76 | messages.put(messageConfig.getMessageId(), messageConfig); 77 | } 78 | 79 | /** 80 | * Returns this configuration as XML. 81 | * 82 | * @return 83 | * The configuration in XML Format. 84 | */ 85 | public String toXMLString() { 86 | StringBuilder sb = new StringBuilder(); 87 | 88 | sb.append(String.format("%n", XML_CONFIG_NAME)); 89 | sb.append(String.format("\t%s%n", "MessageIdType", messageIdType )); 90 | for (MessageConfig message : messages.values()) { 91 | sb.append(String.format("%s%n", message.toXMLString())); 92 | } 93 | sb.append(""); 94 | 95 | return sb.toString(); 96 | } 97 | 98 | public void setParserWarnings(String parserWarnings) { 99 | if (parserWarnings != null && !parserWarnings.isEmpty()) 100 | this.parserWarnings=parserWarnings; 101 | } 102 | 103 | public String getParserWarnings() { 104 | return parserWarnings; 105 | } 106 | 107 | /** 108 | * Parses an XML configuration. 109 | * 110 | * @param xml 111 | * The XML configuration as created by toXMLString method. 112 | * @return 113 | * The parsed configuration. 114 | * @throws Exception 115 | */ 116 | public static DriverConfig fromXMLString(String xml) throws Exception { 117 | 118 | if (xml == null || xml.equals("")) 119 | return null; 120 | 121 | XMLConfigParser parser = new XMLConfigParser(); 122 | DriverConfig parsed; 123 | try { 124 | parsed = (DriverConfig) parser.parseXML(DriverConfig.class, DriverConfig.XML_CONFIG_NAME, xml); 125 | } catch (Exception e) { 126 | throw e; 127 | } 128 | 129 | parsed.setParserWarnings(parser.getWarnings()); 130 | 131 | return parsed; 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/DriverSettings.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.meta.config; 2 | 3 | import java.nio.ByteOrder; 4 | 5 | import com.chitek.ignition.drivers.generictcp.types.OptionalDataType; 6 | 7 | /** 8 | * This class wraps general driver settings. In addition, the messageIdType is also availabe from this class though it is not a 9 | * setting, but configured with the message. 10 | * 11 | */ 12 | public class DriverSettings implements IDriverSettings { 13 | private String hostname; 14 | private int port; 15 | private final boolean connectOnStartup; 16 | private final int timeout; 17 | private final int messageTimeout; 18 | private final ByteOrder byteOrder; 19 | private final int timestampFactor; 20 | private final long maxTimestamp; 21 | private final OptionalDataType messageIdType; 22 | 23 | public DriverSettings( 24 | String hostname, 25 | int port, 26 | boolean connectOnStartup, 27 | int timeout, 28 | int packetTimeout, 29 | boolean reverseByteOrder, 30 | int timestampFactor, 31 | long maxTimestamp, 32 | OptionalDataType messageIdType) 33 | { 34 | this.hostname = hostname; 35 | this.port = port; 36 | this.connectOnStartup = connectOnStartup; 37 | this.timeout = timeout; 38 | this.messageTimeout = packetTimeout; 39 | this.byteOrder = reverseByteOrder ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN; 40 | this.timestampFactor = timestampFactor; 41 | this.maxTimestamp = maxTimestamp; 42 | this.messageIdType = messageIdType; 43 | } 44 | 45 | public String getHostname() { 46 | return hostname; 47 | } 48 | 49 | public void setHostname(String hostname) { 50 | this.hostname = hostname; 51 | } 52 | 53 | public int getPort() { 54 | return port; 55 | } 56 | 57 | public void setPort(int port) { 58 | this.port = port; 59 | } 60 | 61 | public boolean isConnectOnStartup() { 62 | return connectOnStartup; 63 | } 64 | 65 | /** If no data is received for more than the time given here, the driver will disconnect 66 | * an try to reconnect. 0 disables this function. 67 | * 68 | * @return 69 | * The timeout in ms. 70 | */ 71 | public int getTimeout() { 72 | return timeout; 73 | } 74 | 75 | @Override 76 | public int getMessageTimeout() { 77 | return messageTimeout; 78 | } 79 | 80 | @Override 81 | public ByteOrder getByteOrder() { 82 | return byteOrder; 83 | } 84 | 85 | @Override 86 | public int getTimestampFactor() { 87 | return timestampFactor; 88 | } 89 | 90 | @Override 91 | public long getMaxTimestamp() { 92 | return maxTimestamp; 93 | } 94 | 95 | /** 96 | * @return 97 | * The message id type from the message config. This is no general setting, but 98 | * added here for convenience. 99 | */ 100 | @Override 101 | public OptionalDataType getMessageIdType() { 102 | return messageIdType; 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/DriverSettingsPassive.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.meta.config; 2 | 3 | import java.nio.ByteOrder; 4 | import java.util.List; 5 | 6 | import com.chitek.ignition.drivers.generictcp.types.OptionalDataType; 7 | import com.chitek.ignition.drivers.generictcp.types.RemoteDevice; 8 | 9 | /** 10 | * This class wraps general driver settings. In addition, the messageIdType is also availabe from this class though it is not a 11 | * setting, but configured with the message. 12 | * 13 | */ 14 | public class DriverSettingsPassive implements IDriverSettings { 15 | private final String serverHostname; 16 | private final int serverPort; 17 | private final int timeout; 18 | private final boolean useUdp; 19 | private final boolean acceptAll; 20 | private final List devices; 21 | private final int messageTimeout; 22 | private final ByteOrder byteOrder; 23 | private final int timestampFactor; 24 | private final long maxTimestamp; 25 | private final OptionalDataType messageIdType; 26 | 27 | public DriverSettingsPassive( 28 | String serverHostname, 29 | int serverPort, 30 | int timeout, 31 | boolean useUdp, 32 | boolean acceptAll, 33 | List devices, 34 | int packetTimeout, 35 | boolean reverseByteOrder, 36 | int timestampFactor, 37 | long maxTimestamp, 38 | OptionalDataType messageIdType) 39 | { 40 | this.serverHostname = serverHostname; 41 | this.serverPort = serverPort; 42 | this.timeout=timeout; 43 | this.useUdp = useUdp; 44 | this.acceptAll = acceptAll; 45 | this.devices = devices; 46 | this.messageTimeout = packetTimeout; 47 | this.byteOrder = reverseByteOrder ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN; 48 | this.timestampFactor = timestampFactor; 49 | this.maxTimestamp = maxTimestamp; 50 | this.messageIdType = messageIdType; 51 | } 52 | 53 | public String getServerAddress() { 54 | return serverHostname; 55 | } 56 | 57 | public int getServerPort() { 58 | return serverPort; 59 | } 60 | 61 | /** If no data is received for more than the time given here, the driver will disconnect. 62 | * 0 disables this function. 63 | * 64 | * @return 65 | * The timeout in ms. 66 | */ 67 | public long getTimeout() { 68 | return timeout*1000; 69 | } 70 | 71 | public boolean getUseUdp() { 72 | return useUdp; 73 | } 74 | 75 | /** 76 | * @return 77 | * true if this device should ignore the devices list and accept all incoming connections 78 | */ 79 | public boolean getAcceptAll() { 80 | return acceptAll; 81 | } 82 | 83 | public List getDevices() { 84 | return devices; 85 | } 86 | 87 | /** 88 | * The maximum time between two parts of a data package. If a package is not completed in 89 | * the time given here, incoming data will be discarded. 90 | * 91 | * @return 92 | */ 93 | @Override 94 | public int getMessageTimeout() { 95 | return messageTimeout; 96 | } 97 | 98 | @Override 99 | public ByteOrder getByteOrder() { 100 | return byteOrder; 101 | } 102 | 103 | @Override 104 | public int getTimestampFactor() { 105 | return timestampFactor; 106 | } 107 | 108 | @Override 109 | public long getMaxTimestamp() { 110 | return maxTimestamp; 111 | } 112 | 113 | /** 114 | * @return 115 | * The message id type from the message config. This is no general setting, but 116 | * added here for convenience. 117 | */ 118 | @Override 119 | public OptionalDataType getMessageIdType() { 120 | return messageIdType; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/HeaderTagConfig.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.meta.config; 17 | 18 | import java.io.Serializable; 19 | 20 | import com.chitek.ignition.drivers.generictcp.types.HeaderDataType; 21 | 22 | @SuppressWarnings("serial") 23 | public class HeaderTagConfig implements Serializable { 24 | 25 | private static final String XML_CONFIG_NAME = "HeaderTagConfig"; 26 | 27 | private HeaderDataType dataType; 28 | 29 | /** The input is stored as a raw String to support hex input */ 30 | private String rawValue; 31 | 32 | /** The value for fixed value tags */ 33 | private transient int value; 34 | 35 | /** The size if this tag is an array */ 36 | private int size; 37 | 38 | /** Byte offset in message - has to be set by the using class*/ 39 | private transient int offset; 40 | 41 | public HeaderTagConfig() { 42 | this(HeaderDataType.Dummy, 0, 1); 43 | } 44 | 45 | public HeaderTagConfig(HeaderDataType dataType) { 46 | this(dataType, 0, 1); 47 | } 48 | 49 | public HeaderTagConfig(HeaderDataType dataType, int value, int size) { 50 | this.dataType = dataType; 51 | this.value = value; 52 | this.rawValue = String.valueOf(value); 53 | this.size = size; 54 | } 55 | 56 | public HeaderDataType getDataType() { 57 | return dataType; 58 | } 59 | 60 | /** 61 | * Method used by XML-Parser 62 | * * 63 | * @param dataTypeName 64 | */ 65 | public void setDataType(String dataTypeName) { 66 | this.dataType = HeaderDataType.valueOf(dataTypeName); 67 | } 68 | 69 | /** 70 | * 71 | * @return 72 | * The raw input string 73 | */ 74 | public String getRawValue() { 75 | return rawValue != null ? rawValue : String.valueOf(value); 76 | } 77 | 78 | public void setRawValue(String rawValue) { 79 | this.rawValue = rawValue.trim().equalsIgnoreCase("null") ? "0" : rawValue.trim(); 80 | try { 81 | this.value = Integer.decode(rawValue); 82 | } catch (NumberFormatException e) { 83 | this.value = 0; 84 | } 85 | } 86 | 87 | public int getValue() { 88 | return value; 89 | } 90 | 91 | /** 92 | * 93 | * @return 94 | * The array size or 1 for scalar tags. 95 | */ 96 | public int getSize() { 97 | if (dataType.isArrayAllowed() ) 98 | return size; 99 | else 100 | return 1; 101 | } 102 | 103 | public void setSize(int size) { 104 | this.size = size; 105 | } 106 | 107 | /** 108 | * The length of the data type in bytes. 109 | * 110 | * @return 111 | * The length in bytes 112 | */ 113 | public int getByteCount() { 114 | return dataType.getByteCount(); 115 | } 116 | 117 | /** 118 | * 119 | * @return The byte offset of this tag. Has to be set using {@link #setOffset} 120 | */ 121 | public int getOffset() { 122 | return offset; 123 | } 124 | 125 | /** 126 | * Set the byte offest of this tag. Only used for display in the list editor 127 | * 128 | * @param offset 129 | */ 130 | public void setOffset(int offset) { 131 | this.offset = offset; 132 | } 133 | 134 | /** 135 | * Returns this configuration as XML. 136 | * 137 | * @return 138 | * The configuration in XML Format. 139 | */ 140 | public String toXMLString() { 141 | StringBuilder sb = new StringBuilder(); 142 | 143 | sb.append(String.format("%n", XML_CONFIG_NAME)); 144 | sb.append(String.format("\t%s%n", "DataType", dataType.name() )); 145 | sb.append(String.format("\t%s%n", "RawValue", rawValue )); 146 | sb.append(String.format("\t%s%n", "Size", size )); 147 | sb.append(""); 148 | 149 | return sb.toString(); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/IDriverSettings.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.meta.config; 2 | 3 | import java.nio.ByteOrder; 4 | 5 | import com.chitek.ignition.drivers.generictcp.types.OptionalDataType; 6 | 7 | public interface IDriverSettings { 8 | public ByteOrder getByteOrder(); 9 | 10 | public int getTimestampFactor(); 11 | 12 | public long getMaxTimestamp(); 13 | 14 | /** 15 | * The maximum time between two parts of a data package. If a package is not completed in 16 | * the time given here, incoming data will be discarded (or forwarded in packet based mode). 17 | * 18 | * @return 19 | */ 20 | public int getMessageTimeout(); 21 | 22 | public OptionalDataType getMessageIdType(); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/TagConfig.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.meta.config; 17 | 18 | import java.io.Serializable; 19 | 20 | import com.chitek.ignition.drivers.generictcp.types.BinaryDataType; 21 | import com.chitek.ignition.drivers.generictcp.types.TagLengthType; 22 | 23 | @SuppressWarnings("serial") 24 | public class TagConfig implements Serializable { 25 | 26 | private static final String XML_CONFIG_NAME = "TagConfig"; 27 | 28 | private int id; 29 | private BinaryDataType dataType; 30 | private String alias; 31 | private int size; // Array size of this tag 32 | private TagLengthType lengthType = TagLengthType.FIXED_LENGTH; 33 | 34 | private int offset; // Byte offest in message - just informational in config ui 35 | 36 | // Set a default datatype for new items 37 | public TagConfig() 38 | { 39 | dataType = BinaryDataType.Int16; 40 | size = 1; 41 | } 42 | 43 | public int getId() { 44 | if (dataType.isSpecial()) 45 | return dataType.getSpecialId(); 46 | else 47 | return id; 48 | } 49 | 50 | public void setId(int id) { 51 | this.id = id; 52 | } 53 | 54 | public int getOffset() { 55 | return offset; 56 | } 57 | 58 | public void setOffset(int offset) { 59 | this.offset = offset; 60 | } 61 | 62 | public BinaryDataType getDataType() { 63 | return dataType; 64 | } 65 | 66 | public void setDataType(BinaryDataType dataType) { 67 | this.dataType = dataType; 68 | } 69 | 70 | /** 71 | * Method used by XML-Parser 72 | * * 73 | * @param dataTypeName 74 | */ 75 | public void setDataType(String dataTypeName) { 76 | this.dataType = BinaryDataType.valueOf(dataTypeName); 77 | } 78 | 79 | public TagLengthType getTagLengthType() { 80 | return lengthType; 81 | } 82 | 83 | public void setTagLengthType(TagLengthType tagLengthType) { 84 | this.lengthType = tagLengthType; 85 | } 86 | 87 | /** 88 | * Method used by XML-Parser 89 | * * 90 | * @param dataTypeName 91 | */ 92 | public void setTagLengthType(String tagLengthTypeName) { 93 | this.lengthType = TagLengthType.valueOf(tagLengthTypeName); 94 | } 95 | 96 | public String getAlias() { 97 | if (!dataType.isSpecial()) 98 | return alias; 99 | else 100 | return "_" + dataType.name(); 101 | } 102 | 103 | public void setAlias(String alias) { 104 | this.alias = alias; 105 | } 106 | 107 | /** 108 | * 109 | * @return 110 | * The array size or 1 for scalar tags. Special tags always have size = 1. 111 | */ 112 | public int getSize() { 113 | if (dataType.isArrayAllowed() ) 114 | return size; 115 | else 116 | return 1; 117 | } 118 | 119 | public void setSize(int size) { 120 | this.size = size < 2 ? 1 : size; 121 | } 122 | 123 | /** 124 | * Returns this configuration as XML. 125 | * 126 | * @return 127 | * The configuration in XML Format. 128 | */ 129 | public String toXMLString() { 130 | StringBuilder sb = new StringBuilder(); 131 | 132 | sb.append(String.format("%n", XML_CONFIG_NAME)); 133 | sb.append(String.format("\t%s%n", "Alias", alias )); 134 | sb.append(String.format("\t%s%n", "Id", id )); 135 | sb.append(String.format("\t%s%n", "DataType", dataType.name() )); 136 | sb.append(String.format("\t%s%n", "Size", size )); 137 | sb.append(String.format("\t%s%n", "TagLengthType", lengthType )); 138 | sb.append(""); 139 | 140 | return sb.toString(); 141 | } 142 | 143 | @Override 144 | public String toString() { 145 | if (dataType.isSpecial()) { 146 | return String.format("Tag '%s' id=%d", dataType.getDisplayString(), dataType.getSpecialId()); 147 | } else { 148 | return String.format("Tag '%s' id=%d", alias, id); 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/WritebackConfig.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.meta.config; 17 | 18 | import java.io.Serializable; 19 | 20 | import com.chitek.ignition.drivers.generictcp.types.OptionalDataType; 21 | import com.chitek.ignition.drivers.generictcp.types.WritebackDataType; 22 | import com.chitek.util.XMLConfigParser; 23 | 24 | @SuppressWarnings("serial") 25 | public class WritebackConfig implements Serializable { 26 | 27 | private static final String XML_CONFIG_NAME = "WritebackConfig"; 28 | 29 | private boolean enableWriteback=false; 30 | /** The byte string that should be send as a prefix */ 31 | private String prefix = "0xff,0xff"; 32 | /** TRUE if a message prefix should be send back to the device */ 33 | private boolean usePrefix=false; 34 | private OptionalDataType messageIdType = OptionalDataType.None; 35 | private WritebackDataType dataType = WritebackDataType.Int32; 36 | private boolean sendInitialValue = false; 37 | private boolean sendOnValueChange = false; 38 | private String initialValue = ""; 39 | private int initialId = 0; 40 | 41 | public boolean isEnabled() { 42 | return enableWriteback; 43 | } 44 | 45 | public void setEnabled(boolean isEnabled) { 46 | this.enableWriteback = isEnabled; 47 | } 48 | 49 | public OptionalDataType getMessageIdType() { 50 | return messageIdType; 51 | } 52 | 53 | public void setMessageIdType(OptionalDataType messageIdType) { 54 | this.messageIdType = messageIdType; 55 | } 56 | 57 | /** 58 | * Method used by XML-Parser 59 | * @param messageLengthName 60 | */ 61 | public void setMessageIdType(String messageIdTypeName) { 62 | this.messageIdType = OptionalDataType.valueOf(messageIdTypeName); 63 | } 64 | 65 | public WritebackDataType getDataType() { 66 | return dataType; 67 | } 68 | 69 | public void setDataType(WritebackDataType dataType) { 70 | this.dataType = dataType; 71 | } 72 | 73 | /** 74 | * Method used by XML-Parser 75 | * @param dataTypeName 76 | */ 77 | public void setDataType(String dataTypeName) { 78 | this.dataType = WritebackDataType.valueOf(dataTypeName); 79 | } 80 | 81 | public boolean getSendOnValueChange() { 82 | return sendOnValueChange; 83 | } 84 | 85 | public void setSendOnValueChange(boolean sendOnValueChange) { 86 | this.sendOnValueChange = sendOnValueChange; 87 | } 88 | 89 | public boolean getSendInitialValue() { 90 | return sendInitialValue; 91 | } 92 | 93 | public void setSendInitialValue(boolean sendInitialValue) { 94 | this.sendInitialValue = sendInitialValue; 95 | } 96 | 97 | public String getInitialValue() { 98 | return initialValue; 99 | } 100 | 101 | public void setInitialValue(String initialValue) { 102 | this.initialValue = initialValue; 103 | } 104 | 105 | public int getInitialId() { 106 | return initialId; 107 | } 108 | 109 | public void setInitialId(int initialId) { 110 | this.initialId = initialId; 111 | } 112 | 113 | public boolean isUsePrefix() { 114 | return usePrefix; 115 | } 116 | 117 | public void setUsePrefix(boolean usePrefix) { 118 | this.usePrefix = usePrefix; 119 | } 120 | 121 | public String getPrefix() { 122 | return prefix.trim(); 123 | } 124 | 125 | public void setPrefix(String prefix) { 126 | this.prefix = prefix; 127 | } 128 | 129 | /** 130 | * Returns this configuration as XML. 131 | * 132 | * @return 133 | * The configuration in XML Format. 134 | */ 135 | public String toXMLString() { 136 | StringBuilder sb = new StringBuilder(); 137 | 138 | sb.append(String.format("%n", XML_CONFIG_NAME)); 139 | sb.append(String.format("\t%s%n", "Enabled", enableWriteback )); 140 | sb.append(String.format("\t%s%n", "MessageIdType", messageIdType.name() )); 141 | sb.append(String.format("\t%s%n", "SendOnValueChange", sendOnValueChange )); 142 | sb.append(String.format("\t%s%n", "SendInitialValue", sendInitialValue )); 143 | sb.append(String.format("\t%s%n", "DataType", dataType.name() )); 144 | sb.append(String.format("\t%s%n", "InitialValue", initialValue )); 145 | sb.append(String.format("\t%s%n", "InitialId", initialId )); 146 | sb.append(String.format("\t%s%n", "UsePrefix", usePrefix )); 147 | sb.append(String.format("\t%s%n", "Prefix", prefix )); 148 | sb.append(""); 149 | 150 | return sb.toString(); 151 | } 152 | 153 | /** 154 | * Parses an XML configuration. 155 | * 156 | * @param xml 157 | * The XML configuration as created by toXMLString method. 158 | * @return 159 | * The parsed configuration. 160 | * @throws Exception 161 | */ 162 | public static WritebackConfig fromXMLString(String xml) throws Exception { 163 | 164 | if (xml == null || xml.equals("")) 165 | return null; 166 | 167 | XMLConfigParser parser = new XMLConfigParser(); 168 | WritebackConfig parsed; 169 | try { 170 | parsed = (WritebackConfig) parser.parseXML(WritebackConfig.class, XML_CONFIG_NAME, xml); 171 | } catch (Exception e) { 172 | throw e; 173 | } 174 | return parsed; 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/AbstractConfigUI.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | ******************************************************************************/ 13 | package com.chitek.ignition.drivers.generictcp.meta.config.ui; 14 | 15 | import org.apache.wicket.markup.html.form.Form; 16 | import org.apache.wicket.model.CompoundPropertyModel; 17 | 18 | import com.inductiveautomation.ignition.gateway.web.components.ConfigPanel; 19 | 20 | /** 21 | * Base class for configuration UIs. 22 | * 23 | * @author chi 24 | * 25 | */ 26 | @SuppressWarnings("serial") 27 | public abstract class AbstractConfigUI extends ConfigPanel { 28 | 29 | private final T config; 30 | 31 | public AbstractConfigUI(String panelId, String titleKey, T config) { 32 | super(panelId, titleKey); 33 | 34 | this.config = config; 35 | setDefaultModel(new CompoundPropertyModel(config)); 36 | } 37 | 38 | /** 39 | * @return 40 | * The form that should be submitted when switching tabs 41 | */ 42 | public abstract Form getForm(); 43 | 44 | /** 45 | * @return 46 | * The configuration 47 | */ 48 | public T getConfig() { 49 | return config; 50 | } 51 | 52 | @Override 53 | protected boolean isTitleVisible() { 54 | return false; 55 | } 56 | 57 | @Override 58 | public String[] getMenuPath() { 59 | return new String[0]; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/ConfigUiTabs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 |
11 | [tabbed panel will be here] 12 |
13 | 14 | 15 |
16 |
17 | 18 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/HeaderConfigUI.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 |
10 |
11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | 36 | 42 | 43 | 44 |
Use Header 21 |

useHeader.Description

Size includes header 26 |

sizeIncludesHeader.Description

Use Handshake 31 |

useHandshake.Description

35 | Handshake Message 37 | 38 |

39 | handshakeMsg.Description 40 |

41 |
45 | 46 | 47 | 48 | 49 | 52 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 68 | 70 | 72 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 |
50 | Offset 51 | 53 | Data Type 54 | ValueSize
11
85 | 86 |
87 |
88 | 89 | 90 |
91 | 92 |
93 | 94 | 95 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/HeaderConfigUI.properties: -------------------------------------------------------------------------------- 1 | HeaderDataType.Dummy=Dummy 2 | HeaderDataType.Byte=Fixed Byte 3 | HeaderDataType.Word=Fixed Word 4 | HeaderDataType.PacketSize=Packet Size 5 | HeaderDataType.Timestamp=Packet Timestamp 6 | HeaderDataType.SequenceId=Sequence Id 7 | 8 | offsetlabel=Offset 9 | datatypelabel=Data Type 10 | valuelabel=Value 11 | sizelabel=Size 12 | 13 | useHeader.DisplayName=Use Header 14 | useHeader.Description=If true, all incoming packets are expected to start with the header defined here. 15 | sizeIncludesHeader.DisplayName=Packet sizes includes header 16 | sizeIncludesHeader.Description=If true, the packet size received with the header includes the length of the header itself. 17 | useHandshake.DisplayName=Use Handshake Message 18 | useHandshake.Description=If true, the defined handshake message will be sent back to the device after the message has been evaluated. 19 | handshakeMsg.DisplayName=Handshake Message 20 | handshakeMsg.Description=The handshake message to send. A comma separated list of bytes. Decimal, hexadecimal, and octal numbers are accepted. The strings 'timestamp' and 'sequence' are replaced by the received value, 'lenb' (Byte) and 'lenw' (Word) are replaced with the message length.
Example: 'lenw, 0xfe, 0xfe, timestamp' 21 | 22 | noheaderitems=There are currently no tags configured for the header... 23 | 24 | # Validator messages 25 | rawValue.ParseableValidator=The input string '${input}' can not be parsed as an Integer. 26 | rawValue.RangeValidator=Value must be between ${minimum} and ${maximum}. 27 | dataType.SpecialTypesValidator=Special DataTypes must be used only once in a message. 28 | size.RangeValidator=Array size must be between ${minimum} and ${maximum}. 29 | useHeader.NoSizeTag=To use the header there must be a tag with the packet size. 30 | handshakeMsg.ByteStringValidator=Handshake can not be parsed (${error}). -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/HeaderConfigUI_de.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/HeaderConfigUI_de.properties -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/MessageConfigUI.properties: -------------------------------------------------------------------------------- 1 | # Types for Dropdowns 2 | BinaryDataType.Dummy=Dummy 3 | BinaryDataType.UByte=Unsigned Byte 4 | BinaryDataType.Byte=Signed Byte 5 | BinaryDataType.Bool8=Bool8 6 | BinaryDataType.Bool16=Bool16 7 | BinaryDataType.UInt16=UInt16 8 | BinaryDataType.Int16=Int16 9 | BinaryDataType.UInt32=UInt32 10 | BinaryDataType.Int32=Int32 11 | BinaryDataType.Float=Float 12 | BinaryDataType.String=String 13 | BinaryDataType.RawString=Raw String 14 | BinaryDataType.MessageAge=Message Age 15 | 16 | QueueMode.NONE=None 17 | QueueMode.HANDSHAKE=Handshake 18 | QueueMode.DELAYED=Delayed 19 | 20 | MessageType.FIXED_LENGTH=Fixed length 21 | MessageType.PACKET_BASED=Packet based 22 | 23 | TagLengthType.FIXED_LENGTH=Fixed 24 | TagLengthType.PACKET_BASED=From Packet 25 | 26 | selectMessageLabel=Select Message 27 | 28 | info.deleted=Message with ID {0} has been deleted. 29 | info.new=Created new message with ID {0}. 30 | info.copy=Copied message to new ID {0}. 31 | info.import=Successfully imported message with new ID {0}. 32 | 33 | words.new=New 34 | 35 | warn.noMessageId0=Message ID Type 'None' is selected, but there is no message with ID '0' defined. 36 | warn.MessageIdGT255=Message ID Type is 'Byte' but there is a message with ID > 255 defined. That message will be ignored. 37 | warn.noFileToUpload=No file was selected for upload. 38 | warn.importIdChanged=Message Id {0} in imported configuration is already in use. 39 | 40 | messageIdType.DisplayName=Type of message ID 41 | messageIdType.Description=Select Byte or Word to use multiple messages. When 'None' is selected, the driver will only use message with ID 0. 42 | messageType.DisplayName=Message type 43 | messageType.Description='Packet based' for message that may vary in length. One tag has to be configured with 'Packet Based' length. Data is read into this tag until the message end is detected by expiration of the Packet Timeout. 44 | queueMode.DisplayName=Queue Mode 45 | queueMode.Description='Handshake' and 'Delayed' modes buffer incoming messages in memory. The OPC values are updated only after an OPC client writes to the handshake tag (Handshake Mode) or after a fixed delay (Delayed Mode). 46 | usePersistance.DisplayName=Use Persistence 47 | usePersistance.Description=If true, the message queue (in Handshake or Delayed mode) will use a persistent disk storage to prevent data loss. If false, the queue will only be saved on a regular shutdown. 48 | 49 | messageLabel=Message ID 50 | idlabel=ID 51 | offsetlabel=Offset 52 | aliaslabel=Alias 53 | sizelabel=Size 54 | datatypelabel=Data Type 55 | 56 | noitems=There are currently no tags configured in this message... 57 | 58 | import.linklabel=Import Message 59 | import.error=Error importing message configuration from XML file. 60 | export.linklabel=Export Message 61 | 62 | # Validator messages 63 | UniqueMessageIdValidator=Message ID must be unique. 64 | messageId.RangeValidator=Message ID must be between ${minimum} and ${maximum}. 65 | UniqueMessageAliasValidator=Message Alias must be unique. 66 | messageAlias.PatternValidator=Alias must consist of alphanumerics and underscores. 67 | messageAlias.StringValidator.range=Alias must be between ${minimum} and ${maximum} characters long. 68 | dataType.SpecialTypesValidator=Special DataTypes must be used only once in a message. 69 | id.UniqueValueValidator=ID must be unique. 70 | id.RangeValidator=ID must be between ${minimum} and ${maximum}. 71 | size.RangeValidator=Array size must be between ${minimum} and ${maximum}. 72 | alias.UniqueValueValidator=Alias must be unique. 73 | alias.PatternValidator=Alias must consist of alphanumerics and underscores. 74 | alias.StringValidator.range=Alias must be between ${minimum} and ${maximum} characters long. 75 | alias.StringValidator.noMatch=The names of special tags "Handshake", "MessageCount", "QueueSize" and "Timestamp" must not be used as an alias. 76 | tagLengthType.OnlyOnePackedBasedValidator=There must be only one tag defined with a packet based length. 77 | error.noPacketBasedLengthTag=The message is configured to use packet base length, but there's no tag with a packet based length. -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/MessageConfigUI_de.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/MessageConfigUI_de.properties -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/SessionConfig.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.meta.config.ui; 2 | 3 | import java.io.Serializable; 4 | 5 | import com.chitek.ignition.drivers.generictcp.meta.config.DriverConfig; 6 | import com.chitek.ignition.drivers.generictcp.meta.config.HeaderConfig; 7 | import com.chitek.ignition.drivers.generictcp.meta.config.WritebackConfig; 8 | 9 | /** 10 | * This class is used to store the edited configuration in the session. 11 | */ 12 | public class SessionConfig implements Serializable { 13 | private static final long serialVersionUID = 1L; 14 | 15 | private DriverConfig driverConfig; 16 | private WritebackConfig writebackConfig; 17 | private HeaderConfig headerConfig; 18 | 19 | public SessionConfig(DriverConfig driverConfig, WritebackConfig writebackConfig, HeaderConfig headerConfig) { 20 | this.driverConfig = driverConfig; 21 | this.writebackConfig = writebackConfig; 22 | this.headerConfig = headerConfig; 23 | } 24 | 25 | public DriverConfig getDriverConfig() { 26 | return driverConfig; 27 | } 28 | 29 | public WritebackConfig getWritebackConfig() { 30 | return writebackConfig; 31 | } 32 | 33 | public HeaderConfig getHeaderConfig() { 34 | return headerConfig; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/WritebackConfigUI.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 24 | 25 | 26 |
19 | enable 21 | 22 |

enable.Description

23 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 43 | 44 | 45 | 46 | 48 | 49 | 50 | 52 | 58 | 59 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 75 | 76 | 78 | 79 | 80 | 82 | 88 | 89 | 90 | 92 | 98 | 99 | 100 |
36 | messageId 38 |
Use Prefix 47 |

usePrefix.Description

51 | Prefix 53 | 54 |

55 | prefix.Description 56 |

57 |
61 | DataType 63 |
sendOnValueChange.DisplayName 72 |

sendOnValueChange.Description

sendOnConnect.DisplayName 77 |

sendOnConnect.Description

81 | Initial Value 83 | 84 |

85 | initialValue.Description 86 |

87 |
91 | Initial Id 93 | 94 |

95 | initialId.Description 96 |

97 |
101 |
102 |
103 |
104 | 105 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/WritebackConfigUI.properties: -------------------------------------------------------------------------------- 1 | WritebackDataType.UInt16=UInt16 2 | WritebackDataType.Int16=Int16 3 | WritebackDataType.UInt32=UInt32 4 | WritebackDataType.Int32=Int32 5 | WritebackDataType.ByteString=ByteString 6 | 7 | # Settings 8 | enabled.DisplayName=Enable write 9 | enabled.Description=Check this box to enable writing to the device. 10 | messageId.DisplayName=Message Id 11 | messageId.Description=Select Byte or Word to send a message ID in the prefix. 12 | usePrefix.DisplayName=Use Prefix 13 | usePrefix.Description=The configured prefix will be send at the beginning of the outgoing message. 14 | prefix.DisplayName=Prefix 15 | prefix.Description=A comma separated list of bytes. Decimal, hexadecimal, and octal numbers are accepted. To send the message length use the strings 'lenb' (Byte) or 'lenw' (Word). The String 'id' is replaced by the message Id set by the client.
Example: 'lenb, 0xfe, 0xfe, id' 16 | dataType.DisplayName=Data Type 17 | dataType.Description=Select the DataType to be used in the outgoing message. 18 | sendOnValueChange.DisplayName=Send on value change 19 | sendOnValueChange.Description=Send to device immediately if an OPC client changes the value. If false, send must be triggered explicitly with the Write tag. 20 | sendInitialValue.DisplayName=Send initial value 21 | sendInitialValue.Description=Sends an initial value after the connection has been established. 22 | initialValue.DisplayName=Initial Value 23 | initialValue.Description=The initial value to send on connect. 24 | initialValue.DataTypeValidator=Initial value can not be parsed as ${messageDataType} 25 | initialValue.RangeValidator=Initial value ist not in valid range for ${messageDataType} 26 | initialId.DisplayName=Initial ID 27 | initialId.Description=The initial ID to send on connect (if Message Id is used). 28 | initialId.RangeValidator=Initial ID value ist not in valid range for ${idDataType} 29 | prefix.ByteStringValidator=Prefix can not be parsed (${error}). 30 | prefix.IdNone=You configured 'id' to be send in the prefix, but Message ID Type is set to None -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/WritebackConfigUI_de.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/WritebackConfigUI_de.properties -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/actionlinksnewarrow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/actionlinksnewarrow.gif -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/close.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/close.gif -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/configui.css: -------------------------------------------------------------------------------- 1 | div#configcontents table.gentcpActiontable thead { 2 | border: none; 3 | } 4 | 5 | div#configcontents table.gentcpActiontable thead tr td { 6 | border: 1px solid gray; 7 | background-color: #DDD; 8 | } 9 | 10 | 11 | div#configcontents table.gentcpActiontable tbody tr td { 12 | border: 1px solid gray; 13 | vertical-align: middle; 14 | padding: 4px 6px; 15 | } 16 | 17 | /* Show message alias with dark background */ 18 | div#configcontents table.gentcpActiontable tbody td.noedit { 19 | background-color: #DDD; 20 | color: #000; 21 | font-weight: bold; 22 | } 23 | 24 | /* Fixed width for Offset column */ 25 | table.gentcpActiontable td.offset { 26 | width: 5ex; 27 | } 28 | 29 | /* The action links with images */ 30 | div#configcontents table.gentcpActiontable td.links { 31 | padding: 0px; 32 | width: 22px; 33 | background-color: transparent; 34 | border: none; 35 | } 36 | table.gentcpActiontable td.links a { 37 | background-repeat: no-repeat; 38 | background-position: center; 39 | padding-right: 2px; 40 | padding-left: 16px; 41 | } 42 | table.gentcpActiontable td.links a:hover { 43 | border-bottom: 2px solid; 44 | } 45 | table.gentcpActiontable a.insert-link { 46 | background-image: url('row_insert.png'); 47 | } 48 | table.gentcpActiontable a.delete-link { 49 | background-image: url('row_delete.png'); 50 | } 51 | table.gentcpActiontable a.move-up-link { 52 | background-image: url('row_up.png'); 53 | } 54 | table.gentcpActiontable a.move-down-link { 55 | background-image: url('row_down.png'); 56 | } 57 | span.importlink 58 | { 59 | background: url("actionlinksnewarrow.gif") no-repeat scroll 0px center transparent; 60 | padding-left:20px; 61 | } 62 | div.ajax-indicator { 63 | position:absolute; 64 | top:0px; 65 | width: 100%; 66 | height: 100%; 67 | background-color:grey; 68 | background-image: url('indicator.gif'); 69 | background-repeat: no-repeat; 70 | background-position: center center; 71 | } 72 | 73 | div#configcontents select { 74 | height: 30px; 75 | margin: 0px 0px; 76 | } 77 | 78 | div#configcontents input[type=button].gentcp_table_btn { 79 | margin-left: 5px; 80 | margin-right: 5px; 81 | padding: 0.2rem 0.3rem; 82 | float: none; 83 | } 84 | 85 | div#configcontents input, div#configcontents select { 86 | height: 30px; 87 | margin: 0px 0px; 88 | } 89 | 90 | /* The help link */ 91 | div.driverhelplink { 92 | margin-bottom: 5px; 93 | float:left; 94 | } 95 | div.driverhelplink a { 96 | padding-left: 18px; 97 | background: url('help.gif') no-repeat; 98 | } 99 | 100 | /*Feedback Panel warnings has no default CSS in Ignition **************/ 101 | li.feedbackPanelWARNING { 102 | display: block; 103 | list-stile: square inside; 104 | background-color: yellow; 105 | color: black; 106 | padding: 4px; 107 | margin-bottom: 5px; 108 | font-weight: bold; 109 | } 110 | span.feedbackPanelWARNING ul { 111 | margin-left: 30px; 112 | } 113 | span.feedbackPanelWARNING ul li { 114 | display: list-item; 115 | list-style: disc; 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/help.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/help.gif -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/indicator.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/indicator.gif -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/maximize.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/maximize.gif -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/row_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/row_delete.png -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/row_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/row_down.png -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/row_insert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/row_insert.png -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/row_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/res/row_up.png -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/wicket-package.properties: -------------------------------------------------------------------------------- 1 | helpUrl=../../system/moduledocs/chitek.generictcpdriver/manual-frame.html 2 | showHelp=Help 3 | showHelpTitle=Open manual in new window 4 | 5 | submit.save=Save 6 | submit.saveTitle=Save the changed configuration and restart the driver 7 | submit.new=New 8 | submit.newTitle=Create a new message 9 | submit.copy=Copy 10 | submit.copyTitle=Copy the current message 11 | submit.delete=Delete 12 | submit.deleteTitle=Delete this message 13 | submit.back=Back 14 | submit.backTitle=Return without saving 15 | 16 | deletelink.title=Delete this row 17 | insertlink.title=Insert a new row above this 18 | moveuplink.title=Move this row up 19 | movedownlink.title=Move this row down 20 | addrowlink.title=Add Row 21 | 22 | OptionalDataType.None=None 23 | OptionalDataType.UByte=Byte 24 | OptionalDataType.UInt16=Word -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/wicket-package_de.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chi-Ignition/generic-tcp-driver/ff9ad7e84273f42c34df1564568476c7b667e2a9/src/main/java/com/chitek/ignition/drivers/generictcp/meta/config/ui/wicket-package_de.properties -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/tags/ReadableArrayTag.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2019 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.tags; 17 | 18 | import org.eclipse.milo.opcua.stack.core.util.ArrayUtil; 19 | import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue; 20 | import org.eclipse.milo.opcua.stack.core.types.builtin.DateTime; 21 | import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode; 22 | import org.eclipse.milo.opcua.stack.core.types.builtin.Variant; 23 | 24 | import com.chitek.ignition.drivers.generictcp.types.BinaryDataType; 25 | import com.chitek.ignition.drivers.generictcp.util.Util; 26 | 27 | import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ushort; 28 | 29 | /** 30 | * A readable tag with an array as its value. The child tags contain the individual array elements. 31 | * 32 | * @author chi 33 | * 34 | */ 35 | public class ReadableArrayTag extends ReadableTcpDriverTag { 36 | 37 | ReadableTcpDriverTag[] childTags; 38 | ReadableTcpDriverTag childRaw; 39 | protected int valueArrayLength = 0; 40 | protected int childCount = 0; 41 | 42 | public ReadableArrayTag(String address, int id, String alias, int index, BinaryDataType dataType, int arrayLength) 43 | { 44 | super(address, id, alias, index, dataType); 45 | this.value = initialValue; 46 | this.childTags = new ReadableTcpDriverTag[arrayLength]; 47 | this.readSize = arrayLength / dataType.getArrayLength(); 48 | // For 1-dimensional arrays, the values array length is the same as childCount 49 | this.valueArrayLength = arrayLength; 50 | } 51 | 52 | @Override 53 | public void setValue(Variant newValue, StatusCode statusCode, DateTime timestamp) { 54 | 55 | if (newValue.getValue().getClass().isArray() && ArrayUtil.getDimensions(newValue.getValue())[0] != valueArrayLength) { 56 | throw new IllegalArgumentException( 57 | String.format("SetValue in ReadableArray '%s' expects an Variant with array size %d. Argument has array size %d." 58 | ,getAddress(), valueArrayLength, ArrayUtil.getDimensions(newValue.getValue())[0] )); 59 | } 60 | this.value = new DataValue(newValue, statusCode, timestamp, timestamp); 61 | 62 | Object[] value = (Object[]) newValue.getValue(); 63 | 64 | // Set values for childs 65 | // A simple for loop is used here, because it performs better with arrays than a (for x : childTags) 66 | int rawValue=0; 67 | for (int i = 0; i < childCount; i++) { 68 | Variant childValue = Util.makeVariant(value[i], this.getDataType()); 69 | childTags[i].setValue(childValue, statusCode, timestamp); 70 | if ( childRaw!=null && (Boolean)value[i]) rawValue += 1< 30 | * For example, a tag with DataType Bool8 and size 3 will have the following structure: 31 | *
32 |  *ReadableBoolArrayTag - Value {x0.0, x0.1, ..., x3.7}
33 |  *	ReadableArrayTag - Value {x0.0, x0.1, ..., x0.7}
34 |  *		ReadableTag  - Value x0.0
35 |  *		ReadableTag  - Value x0.1
36 |  *		...
37 |  *		ReadableTag  - Value x0.7
38 |  *	ReadableArrayTag - Value {x1.0, x1.1, ..., x1.7}
39 |  *		ReadableTag  - Value x1.0
40 |  *		...
41 |  *	...
42 |  * 
43 | * 44 | * @author chi 45 | * 46 | */ 47 | public class ReadableBoolArrayTag extends ReadableArrayTag { 48 | 49 | 50 | public ReadableBoolArrayTag(String address, int id, String alias, BinaryDataType dataType, int arrayLength) 51 | { 52 | super(address, id, alias, -1, dataType, arrayLength); 53 | 54 | // The 1st level tag of a 2-dimensional array contains all values in one big array 55 | this.readSize = arrayLength; 56 | this.valueArrayLength = arrayLength * dataType.getArrayLength(); 57 | } 58 | 59 | @Override 60 | public void setValue(Variant newValue, StatusCode statusCode, DateTime timestamp) { 61 | 62 | if (newValue.getValue().getClass().isArray() && ArrayUtil.getDimensions(newValue.getValue())[0] != valueArrayLength) { 63 | throw new IllegalArgumentException( 64 | String.format("SetValue in ReadableBoolArray '%s' expects an Variant with array size %d. Argument has array size %d." 65 | ,getAddress(), this.valueArrayLength, ArrayUtil.getDimensions(newValue.getValue())[0] )); 66 | } 67 | this.value = new DataValue(newValue, statusCode, timestamp, timestamp); 68 | 69 | Object[] value = (Object[]) newValue.getValue(); 70 | 71 | // Set values for childs 72 | for (int i = 0; i < childCount; i++) { 73 | 74 | // Set the correct array dimensions for the Variant 75 | int childArraySize = childTags[i].getValueArrayLength(); 76 | 77 | // Special treatment for BOOLEAN 78 | // Ignition API does not support multi-dimensional Variants, so for Boolean tags 79 | // with an array length > 1, the top level tag contains all Booleans in one big array 80 | Boolean[] rawValue = new Boolean[childArraySize]; 81 | System.arraycopy(value, i * childArraySize, rawValue, 0, childArraySize); 82 | 83 | Variant childValue = new Variant(rawValue); 84 | childTags[i].setValue(childValue, statusCode, timestamp); 85 | } 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/tags/ReadableStringTag.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.tags; 17 | 18 | import com.chitek.ignition.drivers.generictcp.types.BinaryDataType; 19 | 20 | public class ReadableStringTag extends ReadableTcpDriverTag { 21 | 22 | private int stringLength; 23 | 24 | public ReadableStringTag(String address, int id, String alias, BinaryDataType dataType, int stringLength) { 25 | super(address, id, alias, dataType); 26 | this.stringLength = stringLength; 27 | } 28 | 29 | /** 30 | * Returns the count of data to read from network. 31 | * 32 | * @return 33 | */ 34 | public int getReadSize() { 35 | return stringLength; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/tags/ReadableTcpDriverTag.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2019 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.tags; 17 | 18 | import org.eclipse.milo.opcua.sdk.server.nodes.UaVariableNode; 19 | import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue; 20 | import org.eclipse.milo.opcua.stack.core.types.builtin.DateTime; 21 | import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode; 22 | import org.eclipse.milo.opcua.stack.core.types.builtin.Variant; 23 | 24 | import com.chitek.ignition.drivers.generictcp.types.BinaryDataType; 25 | import com.inductiveautomation.xopc.driver.api.tags.DynamicDriverTag; 26 | 27 | /** 28 | * A simple tag that is only readable. 29 | * 30 | * @author chi 31 | * 32 | */ 33 | public class ReadableTcpDriverTag extends DynamicDriverTag { 34 | 35 | protected BinaryDataType driverDataType; 36 | protected DataValue value; 37 | private final String alias; 38 | private final int id; 39 | private final int index; 40 | protected UaVariableNode uaNode; 41 | protected int readSize; 42 | 43 | protected static final DataValue initialValue = new DataValue(StatusCode.BAD); 44 | 45 | public ReadableTcpDriverTag(String address, int id, String alias, BinaryDataType dataType) 46 | { 47 | super(address, dataType.getUADataType()); 48 | this.id = id; 49 | this.alias = alias; 50 | this.index = -1; 51 | this.driverDataType = dataType; 52 | this.value = initialValue; 53 | } 54 | 55 | /** 56 | * Constructor for indexed tags. The index is added to browseName and displayName 57 | * 58 | * @param address 59 | * @param id 60 | * @param alias 61 | * @param index 62 | * @param dataType 63 | */ 64 | public ReadableTcpDriverTag(String address, int id, String alias, int index, BinaryDataType dataType) 65 | { 66 | super(address, dataType.getUADataType()); 67 | this.id = id; 68 | this.alias = alias; 69 | this.index = index; 70 | this.driverDataType = dataType; 71 | this.value = initialValue; 72 | } 73 | 74 | public DataValue getValue() { 75 | return value; 76 | } 77 | 78 | public void setValue(StatusCode statusCode) { 79 | this.value = new DataValue(statusCode); 80 | } 81 | 82 | public void setValue(long statusCode) { 83 | this.value = new DataValue(statusCode); 84 | } 85 | 86 | public void setValue(Variant newValue, DateTime timestamp) { 87 | setValue(newValue, StatusCode.GOOD, timestamp); 88 | } 89 | 90 | public void setValue(Variant newValue, long statusCode, DateTime timestamp) { 91 | this.value = new DataValue(newValue, new StatusCode(statusCode), timestamp, this.value.getServerTime()); 92 | } 93 | 94 | public void setValue(Variant newValue, StatusCode statusCode, DateTime timestamp) { 95 | this.value = new DataValue(newValue, statusCode, timestamp, this.value.getServerTime()); 96 | } 97 | 98 | /** 99 | * Sets the value of the corresponding uaNode 100 | */ 101 | public void setUaNodeValue() { 102 | if (uaNode != null) 103 | uaNode.setValue(value); 104 | } 105 | 106 | public void setUaNode(UaVariableNode node) { 107 | this.uaNode = node; 108 | } 109 | 110 | public UaVariableNode getUaNode() { 111 | return uaNode; 112 | } 113 | 114 | public BinaryDataType getDriverDataType() { 115 | return driverDataType; 116 | } 117 | 118 | public String getDisplayName() { 119 | if (index > -1) 120 | return String.format("%s_%02d", alias, index); 121 | else 122 | return alias; 123 | } 124 | 125 | public String getBrowseName() { 126 | if (index > -1) 127 | return String.format("%s[%02d]", alias, index); 128 | else 129 | return alias; 130 | } 131 | 132 | public int getId() { 133 | return id; 134 | } 135 | 136 | /** 137 | * Returns the length of the array length of this tags OPC Value. 138 | * 139 | * @return 140 | * This values array length, or -1 if this tags value is scalar.
141 | * 142 | */ 143 | public int getValueArrayLength() { 144 | return -1; 145 | } 146 | 147 | /** 148 | * Returns the count of data to read from network. 149 | * 150 | * @return 151 | */ 152 | public int getReadSize() { 153 | return 1; 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/tags/WritableTag.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2019 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.tags; 17 | 18 | import org.eclipse.milo.opcua.sdk.core.AccessLevel; 19 | import org.eclipse.milo.opcua.stack.core.BuiltinDataType; 20 | 21 | import com.google.common.collect.ImmutableSet; 22 | import com.inductiveautomation.xopc.driver.api.tags.DynamicDriverTag; 23 | import com.inductiveautomation.xopc.driver.api.tags.WritableDriverTag; 24 | 25 | public abstract class WritableTag extends DynamicDriverTag 26 | implements WritableDriverTag { 27 | 28 | public WritableTag(String address, BuiltinDataType dataType) 29 | { 30 | super(address, dataType); 31 | } 32 | 33 | public ImmutableSet getAccessLevel() { 34 | return (AccessLevel.READ_WRITE); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/types/BinaryDataType.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2019 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.types; 17 | 18 | import java.io.Serializable; 19 | import java.util.Arrays; 20 | import java.util.List; 21 | 22 | import org.eclipse.milo.opcua.stack.core.BuiltinDataType; 23 | 24 | public enum BinaryDataType implements Serializable { 25 | 26 | Dummy("Dummy",1,1,BuiltinDataType.Byte, true, -1, true, true), 27 | UByte("Unsigned Byte",1,1, BuiltinDataType.Byte), 28 | Byte("Signed Byte",1,1, BuiltinDataType.SByte), 29 | Bool8("Boolean 8Bit", 1, 8, BuiltinDataType.Boolean), 30 | Bool16("Boolean 16Bit", 2, 16, BuiltinDataType.Boolean), 31 | UInt16("Unsigned Int 16Bit", 2, 1, BuiltinDataType.UInt16), 32 | Int16("Integer 16Bit", 2, 1, BuiltinDataType.Int16), 33 | UInt32("Unsigned Int 32Bit", 4, 1, BuiltinDataType.UInt32), 34 | Int32("Integer 32Bit", 4, 1, BuiltinDataType.Int32), 35 | Float("Float", 4, 1,BuiltinDataType.Float), 36 | String("String", 1, 1, BuiltinDataType.String), 37 | RawString("RawString", 1, 1, BuiltinDataType.String), 38 | MessageAge("Message Age", 4, 1, BuiltinDataType.UInt32, true, 0, false, false); 39 | 40 | private String displayString; 41 | private int byteCount; 42 | private int arrayLength; 43 | private BuiltinDataType dataType; 44 | private boolean special; 45 | private int specialId; // Id's <=0 are reserved for special tags 46 | private boolean hidden; // Hidden items will not be added as OPC-Nodes 47 | private boolean arrayAllowed; 48 | 49 | private BinaryDataType(String displayString, int byteSize, int arraySize 50 | , BuiltinDataType dataType) { 51 | 52 | this(displayString, byteSize, arraySize, dataType, false, 0, false, true); 53 | } 54 | 55 | private BinaryDataType(String displayString, int byteSize, int arraySize 56 | , BuiltinDataType dataType, boolean special, int specialId, boolean hidden, boolean arrayAllowed) { 57 | 58 | this.displayString = displayString; 59 | this.byteCount = byteSize; 60 | this.arrayLength = arraySize; 61 | this.dataType = dataType; 62 | this.special = special; 63 | this.specialId = specialId; 64 | this.hidden = hidden; 65 | this.arrayAllowed = arrayAllowed; 66 | } 67 | 68 | public String getDisplayString() { 69 | return displayString; 70 | } 71 | 72 | public int getByteCount() { 73 | return byteCount; 74 | } 75 | 76 | public int getArrayLength() { 77 | return arrayLength; 78 | } 79 | 80 | /** 81 | * Special Items do not allow configuration 82 | * @return 83 | */ 84 | public boolean isSpecial() { 85 | return special; 86 | } 87 | 88 | /** 89 | * Hidden items will not be added as OPC-Nodes and are not accessible by clients. 90 | * @return 91 | * True, if the itme is hidden 92 | */ 93 | public boolean isHidden() { 94 | return hidden; 95 | } 96 | 97 | /** 98 | * @return 99 | * True if this DataType can be configured as an array (size > 1) 100 | */ 101 | public boolean isArrayAllowed() { 102 | return arrayAllowed; 103 | } 104 | 105 | public boolean isString() { 106 | return this==BinaryDataType.String || this==BinaryDataType.RawString; 107 | } 108 | 109 | public int getSpecialId() { 110 | return specialId; 111 | } 112 | 113 | public BuiltinDataType getUADataType() { 114 | return dataType; 115 | } 116 | 117 | /** 118 | * Returns true if the given BinaryDataType supports variable length. 119 | */ 120 | public boolean supportsVariableLength() { 121 | return this == BinaryDataType.Dummy || this == BinaryDataType.String || this == BinaryDataType.RawString; 122 | } 123 | 124 | public static List getOptions() { 125 | return Arrays.asList(BinaryDataType.values()); 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/types/DriverState.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.types; 2 | 3 | import com.chitek.ignition.drivers.generictcp.ModuleHook; 4 | import com.inductiveautomation.ignition.common.BundleUtil; 5 | 6 | public enum DriverState { 7 | Disconnected, Connecting, Connected, Disconnecting, Terminated, Disabled, ConfigError, Listening; 8 | 9 | public String toLocalString() { 10 | return BundleUtil.get().getStringLenient(ModuleHook.BUNDLE_PREFIX + ".State." + this.name()); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/types/HeaderDataType.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.types; 17 | 18 | import java.io.Serializable; 19 | import java.util.ArrayList; 20 | import java.util.Arrays; 21 | import java.util.List; 22 | 23 | /** 24 | * Data types to be used in the message header. 25 | * 26 | * 27 | * @author chi 28 | * 29 | */ 30 | public enum HeaderDataType implements Serializable { 31 | 32 | Dummy(1, false, false, true), 33 | Byte(1, false, true, false), 34 | Word(2, false, true, false), 35 | PacketSize(2, true, false, false), 36 | Timestamp(4, true, false, false), 37 | SequenceId(2, true, false, false); 38 | 39 | private int byteCount; 40 | private boolean special; 41 | private boolean hasValue; 42 | private boolean arrayAllowed; 43 | 44 | private HeaderDataType(int byteSize, boolean special, boolean hasValue, boolean arrayAllowed) { 45 | 46 | this.byteCount = byteSize; 47 | this.special = special; 48 | this.hasValue = hasValue; 49 | this.arrayAllowed = arrayAllowed; 50 | } 51 | 52 | public int getByteCount() { 53 | return byteCount; 54 | } 55 | 56 | /** 57 | * Special Items are alowed only once in a config 58 | * @return 59 | */ 60 | public boolean isSpecial() { 61 | return special; 62 | } 63 | 64 | /** 65 | * 66 | * @return 67 | * True if this type uses a value. All tags that use a value are treated as fixed values, that have to be present 68 | * in the header with the defined value. 69 | */ 70 | public boolean isHasValue() { 71 | return hasValue; 72 | } 73 | 74 | /** 75 | * @return 76 | * True if this type can be configured as an array (size > 1) 77 | */ 78 | public boolean isArrayAllowed() { 79 | return arrayAllowed; 80 | } 81 | 82 | public static List getOptions() { 83 | return Arrays.asList(HeaderDataType.values()); 84 | } 85 | 86 | public static String[] getSpecialItemNames() { 87 | List result=new ArrayList(); 88 | for (HeaderDataType value:HeaderDataType.values()) { 89 | if (value.isSpecial()) 90 | result.add(value.name()); 91 | } 92 | 93 | return result.toArray(new String[0]); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/types/MessageType.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2014 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.types; 17 | 18 | import java.util.Arrays; 19 | import java.util.List; 20 | 21 | public enum MessageType { 22 | FIXED_LENGTH, PACKET_BASED; 23 | 24 | /** 25 | * List with the options to use in a DropDownChoice 26 | * 27 | * @return 28 | */ 29 | public static List getOptions() { 30 | return Arrays.asList(values()); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/types/OptionalDataType.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2019 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.types; 17 | 18 | import java.util.Arrays; 19 | import java.util.List; 20 | 21 | import org.eclipse.milo.opcua.stack.core.BuiltinDataType; 22 | 23 | import com.chitek.ignition.drivers.generictcp.ModuleHook; 24 | import com.inductiveautomation.ignition.common.BundleUtil; 25 | 26 | public enum OptionalDataType { 27 | None, UByte, UInt16; 28 | 29 | public String toLocalString() { 30 | return BundleUtil.get().getStringLenient(ModuleHook.BUNDLE_PREFIX + "MessageLength." + this.name()); 31 | } 32 | 33 | /** 34 | * List with the options to use in a DropDownChoice 35 | * 36 | * @return 37 | */ 38 | public static List getOptions() { 39 | return Arrays.asList(values()); 40 | } 41 | 42 | public int getByteSize() { 43 | switch (this) { 44 | case None: 45 | return 0; 46 | case UByte: 47 | return 1; 48 | default: 49 | return 2; 50 | } 51 | } 52 | 53 | public BuiltinDataType getUADataType() { 54 | switch (this) { 55 | case None: 56 | case UByte: 57 | return BuiltinDataType.Byte; 58 | case UInt16: 59 | return BuiltinDataType.UInt16; 60 | } 61 | return null; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/types/QueueMode.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.types; 17 | 18 | import java.util.Arrays; 19 | import java.util.List; 20 | 21 | public enum QueueMode { 22 | NONE, HANDSHAKE, DELAYED; 23 | 24 | /** 25 | * List with the options to use in a DropDownChoice 26 | * 27 | * @return 28 | */ 29 | public static List getOptions() { 30 | return Arrays.asList(values()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/types/RemoteDevice.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.types; 2 | 3 | import java.net.InetAddress; 4 | import java.net.InetSocketAddress; 5 | import java.net.UnknownHostException; 6 | 7 | public class RemoteDevice { 8 | 9 | private final String hostname; 10 | private final String alias; 11 | private InetAddress remoteAddress; 12 | private InetSocketAddress remoteSocketAddress; 13 | private int deviceId; 14 | 15 | public RemoteDevice(String hostname, String alias) { 16 | this.hostname = hostname; 17 | this.alias = alias; 18 | } 19 | 20 | public String getHostname() { 21 | return hostname; 22 | } 23 | 24 | public String getAlias() { 25 | return alias; 26 | } 27 | 28 | /** 29 | * Set the numeric id for this device. 30 | * 31 | * @param deviceId 32 | */ 33 | public void setDeviceId(int deviceId) { 34 | this.deviceId = deviceId; 35 | } 36 | 37 | /** 38 | * @return 39 | * The assigned deviceId. The device id has to be assigned using {@link #setDeviceId} before. 40 | */ 41 | public int getDeviceId() { 42 | return deviceId; 43 | } 44 | 45 | /** 46 | * @return 47 | * true if the hostname has already been resolved to an InetAddress. getInetAddress() will return immediatly 48 | * in this case. 49 | */ 50 | public boolean isAddressResolved() { 51 | return remoteAddress != null; 52 | } 53 | 54 | /** 55 | * Returns the configured address for this device. 56 | * @return 57 | */ 58 | public InetAddress getInetAddress() { 59 | if (remoteAddress != null) { 60 | return remoteAddress; 61 | } 62 | 63 | try { 64 | InetAddress address = InetAddress.getByName(getHostname()); 65 | remoteAddress = address; 66 | return remoteAddress; 67 | } catch (UnknownHostException e) { 68 | return null; 69 | } 70 | } 71 | 72 | /** 73 | * Used to store the remote socket after the device has connected to the driver. 74 | * 75 | * @param remoteSocket 76 | * The remote socket address. 77 | */ 78 | public void setRemoteSocketAddress(InetSocketAddress remoteSocket) { 79 | this.remoteSocketAddress = remoteSocket; 80 | } 81 | 82 | public InetSocketAddress getRemoteSocketAddress() { 83 | return remoteSocketAddress; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/types/TagLengthType.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2014 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.types; 17 | 18 | import java.util.Arrays; 19 | import java.util.List; 20 | 21 | public enum TagLengthType { 22 | FIXED_LENGTH, PACKET_BASED; 23 | 24 | /** 25 | * List with the options to use in a DropDownChoice 26 | * 27 | * @return 28 | */ 29 | public static List getOptions() { 30 | return Arrays.asList(values()); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/types/WritebackDataType.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2019 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.types; 17 | 18 | import java.io.Serializable; 19 | import java.util.Arrays; 20 | import java.util.List; 21 | 22 | import org.eclipse.milo.opcua.stack.core.BuiltinDataType; 23 | 24 | 25 | public enum WritebackDataType implements Serializable { 26 | UInt16(2, BuiltinDataType.UInt16), 27 | Int16(2, BuiltinDataType.Int16), 28 | UInt32(4,BuiltinDataType.UInt32), 29 | Int32(4, BuiltinDataType.Int32), 30 | ByteString(0, BuiltinDataType.String); 31 | 32 | private int byteSize; 33 | private BuiltinDataType uaDataType; 34 | 35 | private WritebackDataType(int byteSize, BuiltinDataType dataType) { 36 | this.byteSize = byteSize; 37 | this.uaDataType = dataType; 38 | } 39 | 40 | public int getByteSize() { 41 | return byteSize; 42 | } 43 | 44 | public BuiltinDataType getUADataType() { 45 | return uaDataType; 46 | } 47 | 48 | /** 49 | * List with the options to use in a DropDownChoice 50 | * 51 | * @return 52 | */ 53 | public static List getOptions() { 54 | return Arrays.asList(values()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/ignition/drivers/generictcp/util/TestUtils.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.ignition.drivers.generictcp.util; 17 | 18 | import java.io.ByteArrayOutputStream; 19 | import java.io.IOException; 20 | import java.io.ObjectOutputStream; 21 | 22 | public class TestUtils { 23 | public static int getObjectSize(Object o) { 24 | ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 25 | ObjectOutputStream oOut; 26 | try { 27 | oOut = new ObjectOutputStream(bOut); 28 | oOut.writeObject(o); 29 | oOut.close(); 30 | } catch (IOException e) { 31 | } 32 | 33 | return bOut.toByteArray().length; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/wicket/FeedbackTextField.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.wicket; 17 | 18 | import org.apache.wicket.markup.ComponentTag; 19 | import org.apache.wicket.markup.html.form.TextField; 20 | import org.apache.wicket.model.IModel; 21 | 22 | /** 23 | * This class extends TextField with a error feedback indicator. Validation errors are indicated by applying a custom 24 | * style to the component tag. Default is 'background-color:#ffff00;' 25 | * 26 | * 27 | * @author chi 28 | * 29 | * @param 30 | * The model object type 31 | */ 32 | public class FeedbackTextField extends TextField { 33 | private static final long serialVersionUID = 1L; 34 | private String style = "background-color:#ffff00;"; 35 | 36 | public FeedbackTextField(String id) { 37 | super(id); 38 | } 39 | 40 | public FeedbackTextField(String id, Class type) { 41 | super(id, type); 42 | } 43 | 44 | public FeedbackTextField(String id, IModel model) { 45 | super(id, model); 46 | } 47 | 48 | public FeedbackTextField(String id, IModel model, Class type) { 49 | super(id, model, type); 50 | } 51 | 52 | @Override 53 | protected void onComponentTag(ComponentTag tag) { 54 | super.onComponentTag(tag); 55 | if (hasErrorMessage()) { 56 | String oldStyle = tag.getAttribute("style"); 57 | tag.put("style", style + oldStyle); 58 | } 59 | } 60 | 61 | /** 62 | * Sets the style to apply in case of an validation error. 63 | * 64 | * @param style 65 | * A CSS style, must end with ';' 66 | * @return 67 | * This, for chaining 68 | */ 69 | public FeedbackTextField setStyle(String style) { 70 | this.style = style; 71 | return this; 72 | } 73 | }; 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/wicket/NonMatchStringValidator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.wicket; 17 | 18 | import java.util.Arrays; 19 | 20 | import org.apache.wicket.validation.IValidatable; 21 | import org.apache.wicket.validation.IValidator; 22 | import org.apache.wicket.validation.ValidationError; 23 | 24 | /** 25 | * This validator accepts all Strings that do not match any of the given Strings. 26 | * If the input matches (ignoring case) any of the given Strings, then an error message will 27 | * be generated with the key "StringValidator.noMatch". 28 | * 29 | * @author chi 30 | * 31 | */ 32 | public class NonMatchStringValidator implements IValidator { 33 | private static final long serialVersionUID = 1L; 34 | private String[] matches; 35 | 36 | /** 37 | * 38 | * @param matches 39 | * An array of Strings that will cause validation to fail. 40 | */ 41 | public NonMatchStringValidator(String[] matches) { 42 | this.matches = matches; 43 | } 44 | 45 | @Override 46 | public void validate(IValidatable validatable) { 47 | String value = validatable.getValue(); 48 | for(String match : Arrays.asList(matches)) { 49 | if(value.equalsIgnoreCase(match)) { 50 | ValidationError error = new ValidationError(); 51 | error.addKey("StringValidator.noMatch"); 52 | validatable.error(error); 53 | } 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/wicket/listeditor/EditorListItem.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.wicket.listeditor; 17 | 18 | import org.apache.wicket.markup.repeater.Item; 19 | import org.apache.wicket.model.AbstractReadOnlyModel; 20 | 21 | 22 | public class EditorListItem extends Item 23 | { 24 | private static final long serialVersionUID = 1L; 25 | 26 | public EditorListItem(String id, int index) 27 | { 28 | super(id, index); 29 | setModel(new ListItemModel()); 30 | } 31 | 32 | private class ListItemModel extends AbstractReadOnlyModel 33 | { 34 | private static final long serialVersionUID = 1L; 35 | 36 | @SuppressWarnings("unchecked") 37 | @Override 38 | public T getObject() 39 | { 40 | return ((ListEditor)EditorListItem.this.getParent()).items.get(getIndex()); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/wicket/listeditor/EditorSubmitLink.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.wicket.listeditor; 17 | 18 | import java.util.List; 19 | 20 | import org.apache.wicket.markup.html.form.SubmitLink; 21 | 22 | /** 23 | * Class for edit links to be used with the list editor. 24 | * This class allows to find the ListEditor instance instead of having to pass it into the link. 25 | * 26 | */ 27 | @SuppressWarnings("serial") 28 | public abstract class EditorSubmitLink extends SubmitLink 29 | { 30 | private transient EditorListItem< ? > parent; 31 | 32 | public EditorSubmitLink(String id) 33 | { 34 | super(id); 35 | setDefaultFormProcessing(false); 36 | } 37 | 38 | protected final EditorListItem< ? > getItem() 39 | { 40 | if (parent == null) 41 | { 42 | parent = findParent(EditorListItem.class); 43 | } 44 | return parent; 45 | } 46 | 47 | protected final List< ? > getList() 48 | { 49 | return getEditor().items; 50 | } 51 | 52 | /** 53 | * Calls ListEditor.addItem, added for convenience 54 | * 55 | * @param value 56 | * @return The new created ListItem 57 | */ 58 | @SuppressWarnings("unchecked") 59 | protected final EditorListItem addItem(T value) { 60 | return ((ListEditor)getEditor()).addItem(value); 61 | 62 | } 63 | 64 | @SuppressWarnings("unchecked") 65 | protected final ListEditor< T > getEditor() 66 | { 67 | return (ListEditor< T >)getItem().getParent(); 68 | } 69 | 70 | 71 | @Override 72 | protected void onDetach() 73 | { 74 | parent = null; 75 | super.onDetach(); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/wicket/listeditor/ListEditor.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.wicket.listeditor; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collections; 20 | import java.util.List; 21 | 22 | import org.apache.wicket.Component; 23 | import org.apache.wicket.markup.html.form.IFormModelUpdateListener; 24 | import org.apache.wicket.markup.repeater.RepeatingView; 25 | import org.apache.wicket.model.IModel; 26 | import org.apache.wicket.util.visit.IVisit; 27 | import org.apache.wicket.util.visit.IVisitor; 28 | 29 | 30 | @SuppressWarnings("serial") 31 | public abstract class ListEditor extends RepeatingView implements IFormModelUpdateListener { 32 | protected List items; 33 | private boolean modelRendered=false; 34 | 35 | public ListEditor(String id, IModel> model) { 36 | super(id, model); 37 | } 38 | 39 | protected abstract void onPopulateItem(EditorListItem item); 40 | 41 | /** 42 | * Adds a new item 43 | * 44 | * @param value 45 | * @return The new created ListItem 46 | */ 47 | public EditorListItem addItem(T value) { 48 | items.add(value); 49 | EditorListItem item = new EditorListItem(newChildId(), items.size() - 1); 50 | add(item); 51 | onPopulateItem(item); 52 | return item; 53 | } 54 | 55 | /** 56 | * Swaps position of to list items. Use this method instead of swap to keep the underlying model in sync. 57 | * 58 | * @param idx1 59 | * index of first component to be swapped 60 | * @param idx2 61 | * index of second component to be swapped 62 | */ 63 | @SuppressWarnings("unchecked") 64 | public void swapItems(int idx1, int idx2) { 65 | int size = size(); 66 | if (idx1 < 0 || idx1 >= size) { 67 | throw new IndexOutOfBoundsException("Argument idx is out of bounds: " + idx1 + "<>[0," 68 | + size + ")"); 69 | } 70 | 71 | if (idx2 < 0 || idx2 >= size) { 72 | throw new IndexOutOfBoundsException("Argument idx is out of bounds: " + idx2 + "<>[0," 73 | + size + ")"); 74 | } 75 | 76 | if (idx1 == idx2) { 77 | return; 78 | } 79 | // Swap the index property of the ListItems 80 | ((EditorListItem) this.get(idx1)).setIndex(idx2); 81 | ((EditorListItem) this.get(idx2)).setIndex(idx1); 82 | 83 | swap(idx1, idx2); 84 | Collections.swap(items, idx1, idx2); 85 | } 86 | 87 | @Override 88 | protected void onBeforeRender() { 89 | if (!modelRendered) { 90 | items = new ArrayList(getModelObject()); 91 | for (int i = 0; i < items.size(); i++) { 92 | EditorListItem li = new EditorListItem(newChildId(), i); 93 | add(li); 94 | onPopulateItem(li); 95 | } 96 | modelRendered=true; 97 | } 98 | super.onBeforeRender(); 99 | } 100 | 101 | public void updateModel() { 102 | setModelObject(items); 103 | } 104 | 105 | /** 106 | * Gets model 107 | * 108 | * @return model 109 | */ 110 | @SuppressWarnings("unchecked") 111 | public final IModel> getModel() { 112 | return (IModel>) getDefaultModel(); 113 | } 114 | 115 | /** 116 | * Sets model 117 | * 118 | * @param model 119 | */ 120 | public final void setModel(IModel> model) { 121 | setDefaultModel(model); 122 | } 123 | 124 | /** 125 | * Gets model object 126 | * 127 | * @return model object 128 | */ 129 | @SuppressWarnings("unchecked") 130 | public final List getModelObject() { 131 | return (List) getDefaultModelObject(); 132 | } 133 | 134 | /** 135 | * Sets model object 136 | * 137 | * @param object 138 | */ 139 | public final void setModelObject(List object) { 140 | setDefaultModelObject(object); 141 | } 142 | 143 | /** 144 | * Forces a new rendering of the child elements after model data has been changed 145 | */ 146 | public final void reloadModel() { 147 | removeAll(); 148 | modelRendered=false; 149 | } 150 | 151 | /** 152 | * Gets the list of items in the ListEditor. 153 | * 154 | * @return 155 | * The list of items 156 | */ 157 | public final List getList() { 158 | return items; 159 | } 160 | 161 | /** 162 | * Returns a List with all child components that match the given id and class. 163 | * 164 | * @param id 165 | * ID of child components to return 166 | * @param clazz 167 | * Ther class of the components to return 168 | * @return 169 | */ 170 | public List getComponentsById(final String id, Class clazz) { 171 | final List list = new ArrayList(this.size()); 172 | 173 | visitChildren(clazz, new IVisitor() { 174 | @Override 175 | public void component(K component, IVisit visit) { 176 | if (component.getId().equals(id)) { 177 | list.add(component); 178 | visit.dontGoDeeper(); 179 | } 180 | } 181 | }); 182 | 183 | return list; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/main/java/com/chitek/wicket/listeditor/UniqueListItemValidator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2012-2013 C. Hiesserich 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | package com.chitek.wicket.listeditor; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Arrays; 20 | import java.util.List; 21 | 22 | import org.apache.wicket.markup.html.form.FormComponent; 23 | import org.apache.wicket.validation.IValidatable; 24 | import org.apache.wicket.validation.IValidator; 25 | import org.apache.wicket.validation.ValidationError; 26 | 27 | 28 | @SuppressWarnings("serial") 29 | public class UniqueListItemValidator implements IValidator { 30 | 31 | private FormComponent parent; 32 | private String messageKey; 33 | private List filter; 34 | private boolean caseSensitive; 35 | 36 | /** 37 | * 38 | * @param parent 39 | * The component this evaluator belongs to 40 | */ 41 | public UniqueListItemValidator(FormComponent parent) { 42 | this(parent, false); 43 | } 44 | 45 | /** 46 | * 47 | * @param parent 48 | * The component this evaluator belongs to 49 | * @param caseSensitive 50 | * True to compare String values case sensitive. Default is false. 51 | */ 52 | public UniqueListItemValidator(FormComponent parent, boolean caseSensitive) { 53 | this.parent = parent; 54 | this.caseSensitive = caseSensitive; 55 | messageKey="UniqueListItemValidator"; 56 | } 57 | 58 | /** 59 | * Set the message 60 | * 61 | * @param messageKey 62 | * @return this for chaining 63 | */ 64 | public UniqueListItemValidator setMessageKey(String messageKey) { 65 | this.messageKey=messageKey; 66 | return this; 67 | } 68 | 69 | /** 70 | * When a filter is set, only values matching one of the filter strings are evaluated. 71 | * 72 | * @param 73 | * Array of Strings to be used as validation filter 74 | * @return this for chaining 75 | */ 76 | public UniqueListItemValidator setFilterList(String[] filter) { 77 | 78 | if (!caseSensitive) { 79 | this.filter = new ArrayList(); 80 | for (int i=0; i validatable) { 96 | 97 | ListEditor editor = parent.findParent(ListEditor.class); 98 | String id = parent.getId(); 99 | 100 | int count = 0; 101 | List fields = editor.getComponentsById(id, FormComponent.class); 102 | 103 | String parentValue = getValue(validatable); 104 | 105 | if (filter != null && caseSensitive && !filter.contains(parentValue)) 106 | return; 107 | if (filter != null && !caseSensitive && !filter.contains(parentValue.toLowerCase())) 108 | return; 109 | if (filter != null && !caseSensitive && !filter.contains(parentValue.toUpperCase())) 110 | return; 111 | 112 | for (FormComponent field : fields) { 113 | String value = field.getRawInput() != null ? field.getRawInput().toString() : null; 114 | 115 | if (value != null && (caseSensitive ? value.equals(parentValue) : value.equalsIgnoreCase(parentValue)) ) { 116 | count++; 117 | } 118 | } 119 | 120 | if (count > 1) { 121 | ValidationError error = new ValidationError(); 122 | error.addKey(messageKey); 123 | validatable.error(error); 124 | } 125 | } 126 | 127 | /** 128 | * Can be overridden to return the String representation of the components value. 129 | * 130 | * The values of the list items are determined using {@link FormComponent#getInput()}, which returns 131 | * the raw input, while the default implementation of getValue returns the ConvertedInput. 132 | * 133 | * @param validatable 134 | * @return 135 | */ 136 | public String getValue(IValidatable validatable) { 137 | return validatable.getValue().toString(); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/test/java/com/chitek/TestUtils/MockExecutor.java: -------------------------------------------------------------------------------- 1 | package com.chitek.TestUtils; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | import java.util.concurrent.Delayed; 6 | import java.util.concurrent.ExecutionException; 7 | import java.util.concurrent.ScheduledFuture; 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.concurrent.TimeoutException; 10 | 11 | public class MockExecutor { 12 | 13 | private final List> scheduledCommands = new LinkedList>(); 14 | 15 | public void executeOnce(Runnable command) { 16 | ScheduledCommand scheduled = new ScheduledCommand(command, 0, TimeUnit.NANOSECONDS); 17 | scheduledCommands.add(scheduled); 18 | } 19 | 20 | public void executeOnce(Runnable command, long delay) { 21 | executeOnce(command, delay, TimeUnit.MILLISECONDS); 22 | } 23 | 24 | public ScheduledFuture executeOnce(Runnable command, long delay, TimeUnit timeUnit) { 25 | ScheduledCommand scheduled = new ScheduledCommand(command, delay, timeUnit); 26 | scheduledCommands.add(scheduled); 27 | return scheduled; 28 | } 29 | 30 | /** 31 | * @return 32 | * The count of pending scheduled commands 33 | */ 34 | public int getScheduledCount() { 35 | return scheduledCommands.size(); 36 | } 37 | 38 | /** 39 | * Remove all scheduled commands. 40 | */ 41 | public void clear() { 42 | scheduledCommands.clear(); 43 | } 44 | 45 | /** 46 | * Run the pending command that was registered first. 47 | * @throws Exception 48 | */ 49 | public void runCommand() throws Exception { 50 | ScheduledCommand scheduled = scheduledCommands.get(0); 51 | scheduled.execute(); 52 | scheduledCommands.remove(scheduled); 53 | } 54 | 55 | public class ScheduledCommand implements ScheduledFuture { 56 | 57 | Runnable command; 58 | long delay; 59 | TimeUnit timeUnit; 60 | private boolean cancelled = false; 61 | private boolean done = false; 62 | 63 | public ScheduledCommand(Runnable command, long delay, TimeUnit timeUnit) { 64 | this.command = command; 65 | this.delay = delay; 66 | this.timeUnit = timeUnit; 67 | } 68 | 69 | public void execute() { 70 | if (cancelled || done) { 71 | throw new RuntimeException("Execute called for a command that has already executed or is cancelled"); 72 | } 73 | 74 | command.run(); 75 | done = true; 76 | } 77 | 78 | public long getDelay() { 79 | return delay; 80 | } 81 | 82 | public TimeUnit getTimeUnit() { 83 | return timeUnit; 84 | } 85 | 86 | @Override 87 | public long getDelay(TimeUnit unit) { 88 | throw new UnsupportedOperationException("MockExecutor: ScheduledFuture#getDelay is not supported"); 89 | } 90 | 91 | @Override 92 | public int compareTo(Delayed o) { 93 | throw new UnsupportedOperationException("MockExecutor: ScheduledFuture#compareTo is not supported"); 94 | } 95 | 96 | @Override 97 | public boolean cancel(boolean mayInterruptIfRunning) { 98 | if (cancelled || done) 99 | return false; 100 | 101 | cancelled = true; 102 | return true; 103 | } 104 | 105 | @Override 106 | public boolean isCancelled() { 107 | return cancelled; 108 | } 109 | 110 | @Override 111 | public boolean isDone() { 112 | return done; 113 | } 114 | 115 | @Override 116 | public V get() throws InterruptedException, ExecutionException { 117 | throw new UnsupportedOperationException("MockExecutor: ScheduledFuture#get is not supported"); 118 | } 119 | 120 | @Override 121 | public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { 122 | throw new UnsupportedOperationException("MockExecutor: ScheduledFuture#get is not supported"); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/test/java/com/chitek/ignition/drivers/generictcp/tests/DriverTestSuite.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.tests; 2 | 3 | import org.apache.log4j.Logger; 4 | import org.junit.runner.RunWith; 5 | import org.junit.runners.Suite; 6 | 7 | import com.chitek.ignition.drivers.generictcp.tests.config.TestConfigParser; 8 | import com.chitek.ignition.drivers.generictcp.tests.folders.TestDeviceStatusFolder; 9 | import com.chitek.ignition.drivers.generictcp.tests.folders.TestFolderManager; 10 | import com.chitek.ignition.drivers.generictcp.tests.folders.TestMessageFolder; 11 | import com.chitek.ignition.drivers.generictcp.tests.folders.TestSimpleWriteFolder; 12 | import com.chitek.ignition.drivers.generictcp.tests.folders.TestSubscription; 13 | import com.chitek.ignition.drivers.generictcp.tests.io.TestMessageState; 14 | import com.chitek.ignition.drivers.generictcp.tests.io.TestNioEventHandler; 15 | 16 | @RunWith(Suite.class) 17 | @Suite.SuiteClasses( 18 | { TestConfigParser.class, 19 | TestMessageState.class, 20 | TestNioEventHandler.class, 21 | TestMessageFolder.class, 22 | TestDeviceStatusFolder.class, 23 | TestSimpleWriteFolder.class, 24 | TestSubscription.class, 25 | TestFolderManager.class}) 26 | 27 | public class DriverTestSuite { 28 | 29 | private static Logger log; 30 | 31 | static { 32 | log = Logger.getRootLogger(); 33 | } 34 | 35 | 36 | public static Logger getLogger() { 37 | return log; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/com/chitek/ignition/drivers/generictcp/tests/MockDriverContext.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.tests; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.concurrent.ScheduledFuture; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | import org.eclipse.milo.opcua.sdk.server.OpcUaServer; 10 | import org.eclipse.milo.opcua.sdk.server.UaNodeManager; 11 | import org.eclipse.milo.opcua.sdk.server.api.NodeManager; 12 | import org.eclipse.milo.opcua.sdk.server.nodes.UaNode; 13 | import org.eclipse.milo.opcua.sdk.server.nodes.UaNodeContext; 14 | import org.eclipse.milo.opcua.sdk.server.nodes.UaObjectNode; 15 | import org.eclipse.milo.opcua.sdk.server.nodes.UaObjectNode.UaObjectNodeBuilder; 16 | import org.eclipse.milo.opcua.sdk.server.nodes.UaVariableNode; 17 | import org.eclipse.milo.opcua.sdk.server.nodes.UaVariableNode.UaVariableNodeBuilder; 18 | import org.eclipse.milo.opcua.stack.core.NamespaceTable; 19 | import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; 20 | 21 | import com.chitek.TestUtils.MockExecutor; 22 | import com.chitek.ignition.drivers.generictcp.IGenericTcpDriverContext; 23 | import com.chitek.ignition.drivers.generictcp.folder.BrowseTree; 24 | import com.inductiveautomation.ignition.common.execution.SchedulingController; 25 | import com.inductiveautomation.ignition.common.execution.SelfSchedulingRunnable; 26 | 27 | public class MockDriverContext implements IGenericTcpDriverContext { 28 | 29 | private final String deviceName; 30 | private final BrowseTree browseTree = new BrowseTree(); 31 | private final Map nodeMap = new HashMap(); 32 | private final Map selfSchedulingRunnables = new HashMap(); 33 | private final MockExecutor executor = new MockExecutor(); 34 | private byte[] lastWrittenMessage; 35 | private String diskPath; 36 | public boolean rescheduleRequested; 37 | private UaNodeContext nodeContext = new MockNodeContext(); 38 | 39 | // Test methods /////////////////////////////////////////////////// 40 | public void setDiskPath(String path) { 41 | diskPath = path; 42 | } 43 | 44 | public BrowseTree getBrowseTree() { 45 | return browseTree; 46 | } 47 | 48 | public UaNode getNode(NodeId nodeId) { 49 | return nodeMap.get(nodeId); 50 | } 51 | 52 | public MockExecutor getExecutor() { 53 | return executor; 54 | } 55 | 56 | public SelfSchedulingRunnable getSelfSchedulingRunnable(String owner, String name) { 57 | return selfSchedulingRunnables.get(owner + name); 58 | } 59 | 60 | /** 61 | * @return 62 | * Returns the message that has been written using writeToRemoteDevice 63 | */ 64 | public byte[] getLastWrittenMessage() { 65 | return lastWrittenMessage; 66 | } 67 | 68 | /////////////////////////////////////////////////////////////////// 69 | 70 | public MockDriverContext(String deviceName) { 71 | this.deviceName = deviceName; 72 | } 73 | 74 | @Override 75 | public String getDeviceName() { 76 | return deviceName; 77 | } 78 | 79 | @Override 80 | public String getDiskPath() { 81 | return diskPath; 82 | } 83 | 84 | @Override 85 | public String getLoggerName() { 86 | return DriverTestSuite.getLogger().getName(); 87 | } 88 | 89 | @Override 90 | public UaNodeContext getNodeContext() { 91 | return nodeContext; 92 | } 93 | 94 | @Override 95 | public UaVariableNodeBuilder getVariableNodeBuilder() { 96 | return UaVariableNode.builder(nodeContext); 97 | } 98 | 99 | @Override 100 | public UaObjectNodeBuilder getObjectNodeBuilder() { 101 | return UaObjectNode.builder(nodeContext); 102 | } 103 | 104 | @Override 105 | public UaNode addNode(UaNode node, String address) { 106 | System.out.println(String.format("addNode: %s", address)); 107 | browseTree.addTag(address); 108 | nodeMap.put(node.getNodeId(), node); 109 | return node; 110 | } 111 | 112 | @Override 113 | public void removeNode(UaNode node) { 114 | 115 | } 116 | 117 | @Override 118 | public void executeOnce(Runnable command) { 119 | executor.executeOnce(command); 120 | } 121 | 122 | @Override 123 | public ScheduledFuture executeOnce(Runnable command, long delay, TimeUnit unit) { 124 | return executor.executeOnce(command, delay, unit); 125 | } 126 | 127 | @Override 128 | public void registerSelfSchedulingRunnable(String owner, String name, SelfSchedulingRunnable command) { 129 | selfSchedulingRunnables.put(owner + name, command); 130 | command.setController(new Controller()); 131 | } 132 | 133 | @Override 134 | public void unregisterScheduledRunnable(String owner, String name) { 135 | SelfSchedulingRunnable command = selfSchedulingRunnables.remove(owner + name); 136 | if (command == null) { 137 | throw new IllegalArgumentException(String.format("The is no registered runnable for %s.%s", owner, name)); 138 | } 139 | } 140 | 141 | @Override 142 | public void writeToRemoteDevice(ByteBuffer message, int deviceId) { 143 | lastWrittenMessage = new byte[message.remaining()]; 144 | message.get(lastWrittenMessage); 145 | } 146 | 147 | @Override 148 | public boolean isActiveNode() { 149 | return true; 150 | } 151 | 152 | private class Controller implements SchedulingController { 153 | @Override 154 | public void requestReschedule(SelfSchedulingRunnable source) { 155 | rescheduleRequested = true; 156 | } 157 | } 158 | 159 | private class MockNodeContext implements UaNodeContext { 160 | 161 | private UaNodeManager nodeManager = new UaNodeManager(); 162 | private NamespaceTable namespaceTable = new NamespaceTable(); 163 | 164 | @Override 165 | public OpcUaServer getServer() { 166 | // TODO Auto-generated method stub 167 | return null; 168 | } 169 | 170 | @Override 171 | public NodeManager getNodeManager() { 172 | return nodeManager; 173 | } 174 | 175 | @Override 176 | public NamespaceTable getNamespaceTable() { 177 | return namespaceTable; 178 | } 179 | 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /src/test/java/com/chitek/ignition/drivers/generictcp/tests/MockExecutionManager.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.tests; 2 | 3 | import java.util.concurrent.ScheduledFuture; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | import com.chitek.TestUtils.MockExecutor; 7 | import com.inductiveautomation.ignition.common.execution.ExecutionManager; 8 | import com.inductiveautomation.ignition.common.execution.SelfSchedulingRunnable; 9 | 10 | public class MockExecutionManager extends MockExecutor implements ExecutionManager { 11 | 12 | @Override 13 | public void register(String paramString1, String paramString2, Runnable paramRunnable, int paramInt) { 14 | throw new UnsupportedOperationException(); 15 | } 16 | 17 | @Override 18 | public void register(String paramString1, String paramString2, Runnable paramRunnable, int paramInt, TimeUnit paramTimeUnit) { 19 | throw new UnsupportedOperationException(); 20 | } 21 | 22 | @Override 23 | public void register(String paramString1, String paramString2, SelfSchedulingRunnable paramSelfSchedulingRunnable) { 24 | throw new UnsupportedOperationException(); 25 | } 26 | 27 | @Override 28 | public void registerWithInitialDelay(String paramString1, String paramString2, Runnable paramRunnable, int paramInt1, int paramInt2) { 29 | throw new UnsupportedOperationException(); 30 | } 31 | 32 | @Override 33 | public void registerWithInitialDelay(String paramString1, String paramString2, Runnable paramRunnable, int paramInt1, TimeUnit paramTimeUnit, int paramInt2) { 34 | throw new UnsupportedOperationException(); 35 | } 36 | 37 | @Override 38 | public void registerAtFixedRate(String paramString1, String paramString2, Runnable paramRunnable, int paramInt, TimeUnit paramTimeUnit) { 39 | throw new UnsupportedOperationException(); 40 | } 41 | 42 | @Override 43 | public void registerAtFixedRateWithInitialDelay(String paramString1, String paramString2, Runnable paramRunnable, int paramInt1, TimeUnit paramTimeUnit, int paramInt2) { 44 | throw new UnsupportedOperationException(); 45 | } 46 | 47 | @Override 48 | public ScheduledFuture scheduleWithFixedDelay(Runnable paramRunnable, long paramLong1, long paramLong2, TimeUnit paramTimeUnit) { 49 | throw new UnsupportedOperationException(); 50 | } 51 | 52 | @Override 53 | public void unRegister(String paramString1, String paramString2) { 54 | throw new UnsupportedOperationException(); 55 | } 56 | 57 | @Override 58 | public void unRegisterAll(String paramString) { 59 | throw new UnsupportedOperationException(); 60 | } 61 | 62 | @Override 63 | public void shutdown() { 64 | throw new UnsupportedOperationException(); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/com/chitek/ignition/drivers/generictcp/tests/TestUtils.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.tests; 2 | 3 | import static org.junit.Assert.assertNotNull; 4 | 5 | import java.io.InputStream; 6 | 7 | import org.apache.commons.io.IOUtils; 8 | 9 | import com.chitek.ignition.drivers.generictcp.meta.config.HeaderConfig; 10 | import com.chitek.ignition.drivers.generictcp.meta.config.MessageConfig; 11 | import com.chitek.ignition.drivers.generictcp.meta.config.WritebackConfig; 12 | 13 | public class TestUtils { 14 | public static MessageConfig readMessageConfig(String configName) throws Exception { 15 | InputStream in = TestUtils.class.getResourceAsStream(configName); 16 | assertNotNull(String.format("Test ressource '%s' not accessible. Check test setup.", configName),in); 17 | String xml = IOUtils.toString(in); 18 | MessageConfig messageConfig = MessageConfig.fromXMLString(xml); 19 | return messageConfig; 20 | } 21 | 22 | public static HeaderConfig readHeaderConfig(String configName) throws Exception { 23 | InputStream in = TestUtils.class.getResourceAsStream(configName); 24 | assertNotNull(String.format("Test ressource '%s' not accessible. Check test setup.", configName), in); 25 | String xml = IOUtils.toString(in); 26 | HeaderConfig headerConfig = HeaderConfig.fromXMLString(xml); 27 | return headerConfig; 28 | } 29 | 30 | public static WritebackConfig readWritebackConfig(String configName) throws Exception { 31 | InputStream in = TestUtils.class.getResourceAsStream(configName); 32 | assertNotNull(String.format("Test ressource '%s' not accessible. Check test setup.", configName),in); 33 | String xml = IOUtils.toString(in); 34 | WritebackConfig writebackConfig = WritebackConfig.fromXMLString(xml); 35 | return writebackConfig; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/chitek/ignition/drivers/generictcp/tests/config/TestConfigParser.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.tests.config; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.io.InputStream; 6 | 7 | import org.apache.commons.io.IOUtils; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | import com.chitek.ignition.drivers.generictcp.meta.config.HeaderConfig; 12 | import com.chitek.ignition.drivers.generictcp.meta.config.HeaderTagConfig; 13 | import com.chitek.ignition.drivers.generictcp.meta.config.MessageConfig; 14 | import com.chitek.ignition.drivers.generictcp.meta.config.TagConfig; 15 | import com.chitek.ignition.drivers.generictcp.tests.DriverTestSuite; 16 | import com.chitek.ignition.drivers.generictcp.types.HeaderDataType; 17 | import com.chitek.ignition.drivers.generictcp.types.MessageType; 18 | import com.chitek.ignition.drivers.generictcp.types.TagLengthType; 19 | 20 | public class TestConfigParser { 21 | 22 | @Before 23 | public void setup() throws Exception { 24 | DriverTestSuite.getLogger(); 25 | } 26 | 27 | @Test 28 | public void testHeaderConfig() throws Exception { 29 | InputStream in = this.getClass().getResourceAsStream("/testHeaderConfig.xml"); 30 | String xml = IOUtils.toString(in); 31 | HeaderConfig headerConfig = HeaderConfig.fromXMLString(xml); 32 | assertEquals("Wrong header size", 4, headerConfig.getHeaderSize()); 33 | assertEquals("Configured header tags",2,headerConfig.getTags().size()); 34 | HeaderTagConfig tag = headerConfig.getFirstFixedTag(); 35 | assertEquals("Type of fixed tag", HeaderDataType.Word, tag.getDataType()); 36 | } 37 | 38 | @Test 39 | public void testMessageConfig() throws Exception { 40 | InputStream in = this.getClass().getResourceAsStream("/testMessageConfig.xml"); 41 | String xml = IOUtils.toString(in); 42 | MessageConfig messageConfig = MessageConfig.fromXMLString(xml); 43 | assertEquals("MessageType", MessageType.FIXED_LENGTH, messageConfig.getMessageType()); 44 | assertEquals("Message id", 1, messageConfig.getMessageId()); 45 | assertEquals("Message length", 4, messageConfig.getMessageLength()); 46 | assertEquals("Configured tags", 2 , messageConfig.tags.size()); 47 | TagConfig tag = messageConfig.tags.get(0); 48 | assertEquals("Tag 1 ID", 1, tag.getId()); 49 | } 50 | 51 | @Test 52 | public void testMessageConfigPacketBased() throws Exception { 53 | InputStream in = this.getClass().getResourceAsStream("/testMessageConfigPacketBased.xml"); 54 | String xml = IOUtils.toString(in); 55 | MessageConfig messageConfig = MessageConfig.fromXMLString(xml); 56 | assertEquals("MessageType", MessageType.PACKET_BASED, messageConfig.getMessageType()); 57 | assertEquals("Message id", 1, messageConfig.getMessageId()); 58 | assertEquals("Message length", 5, messageConfig.getMessageLength()); 59 | assertEquals("Configured tags", 2 , messageConfig.tags.size()); 60 | TagConfig tag = messageConfig.tags.get(1); 61 | assertEquals("TagLengthType", TagLengthType.PACKET_BASED, tag.getTagLengthType()); 62 | } 63 | 64 | @Test 65 | public void testMessageConfigPacketBasedCycle() throws Exception { 66 | InputStream in = this.getClass().getResourceAsStream("/testMessageConfigPacketBased.xml"); 67 | String xml = IOUtils.toString(in); 68 | MessageConfig inputConfig = MessageConfig.fromXMLString(xml); 69 | 70 | String configString = inputConfig.toXMLString(); 71 | MessageConfig messageConfig = MessageConfig.fromXMLString(configString); 72 | 73 | assertEquals("MessageType", MessageType.PACKET_BASED, messageConfig.getMessageType()); 74 | assertEquals("Message id", 1, messageConfig.getMessageId()); 75 | assertEquals("Message length", 5, messageConfig.getMessageLength()); 76 | assertEquals("Configured tags", 2 , messageConfig.tags.size()); 77 | TagConfig tag = messageConfig.tags.get(1); 78 | assertEquals("TagLengthType", TagLengthType.PACKET_BASED, tag.getTagLengthType()); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/test/java/com/chitek/ignition/drivers/generictcp/tests/folders/FolderTestUtils.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.tests.folders; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.chitek.ignition.drivers.generictcp.folder.MessageFolder; 7 | import com.inductiveautomation.xopc.driver.api.items.ReadItem; 8 | import com.inductiveautomation.xopc.driver.api.items.WriteItem; 9 | 10 | public class FolderTestUtils { 11 | public static org.eclipse.milo.opcua.stack.core.types.builtin.DataValue readValue(MessageFolder folder, String address) { 12 | MockReadItem readItem = new MockReadItem(address); 13 | Listitems = new ArrayList(); 14 | items.add(readItem); 15 | folder.readItems(items); 16 | return readItem.getValue(); 17 | } 18 | 19 | public static org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode writeValue(MessageFolder folder, String address, org.eclipse.milo.opcua.stack.core.types.builtin.Variant value) { 20 | List items = new ArrayList(); 21 | items.add(new MockWriteItem(address, value)); 22 | folder.writeItems(items); 23 | return ((MockWriteItem) items.get(0)).getWriteStatus(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/chitek/ignition/drivers/generictcp/tests/folders/MockMessageFolder.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.tests.folders; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import com.chitek.ignition.drivers.generictcp.IGenericTcpDriverContext; 8 | import com.chitek.ignition.drivers.generictcp.folder.MessageFolder; 9 | import com.inductiveautomation.xopc.driver.api.items.SubscriptionItem; 10 | 11 | public class MockMessageFolder extends MessageFolder { 12 | 13 | private Map subscriptions = new HashMap(); 14 | 15 | public MockMessageFolder(IGenericTcpDriverContext driverContext, int folderId, String folderAddress) { 16 | super(driverContext, folderId, folderAddress); 17 | } 18 | 19 | @Override 20 | public void connectionStateChanged(boolean isConnected) { 21 | // Do nothing 22 | 23 | } 24 | 25 | @Override 26 | public void activityLevelChanged(boolean isActive) { 27 | // Do nothing 28 | 29 | } 30 | 31 | @Override 32 | public void changeSubscription(List toAdd, List toRemove) { 33 | if (toAdd != null) { 34 | for (SubscriptionItem item : toAdd) { 35 | subscriptions.put(item.getAddress(), item); 36 | } 37 | } 38 | 39 | if (toRemove != null) { 40 | for (SubscriptionItem item : toRemove) { 41 | subscriptions.remove(item.getAddress()); 42 | } 43 | } 44 | } 45 | 46 | public SubscriptionItem getSubscriptionItem(String address) { 47 | return subscriptions.get(address); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/com/chitek/ignition/drivers/generictcp/tests/folders/MockReadItem.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.tests.folders; 2 | 3 | import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue; 4 | import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; 5 | 6 | import com.inductiveautomation.xopc.driver.api.items.ReadItem; 7 | 8 | public class MockReadItem implements ReadItem { 9 | 10 | private Object addressObject; 11 | private DataValue value; 12 | private final String address; 13 | 14 | public MockReadItem(String address) { 15 | this.address = address; 16 | } 17 | 18 | @Override 19 | public NodeId getNodeId() { 20 | throw new RuntimeException("getNodeId not implemented"); 21 | } 22 | 23 | @Override 24 | public String getAddress() { 25 | return address; 26 | } 27 | 28 | @Override 29 | public Object getAddressObject() { 30 | return addressObject; 31 | } 32 | 33 | @Override 34 | public void setAddressObject(Object paramObject) { 35 | this.addressObject = paramObject; 36 | } 37 | 38 | @Override 39 | public void setValue(DataValue paramDataValue) { 40 | this.value = paramDataValue; 41 | } 42 | 43 | public DataValue getValue() { 44 | return value; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/com/chitek/ignition/drivers/generictcp/tests/folders/MockSubscriptionItem.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.tests.folders; 2 | 3 | import com.inductiveautomation.xopc.driver.api.items.SubscriptionItem; 4 | 5 | public class MockSubscriptionItem extends MockReadItem implements SubscriptionItem { 6 | 7 | private final int samplingRate; 8 | 9 | public MockSubscriptionItem(String address, int samplingRate) { 10 | super(address); 11 | this.samplingRate = samplingRate; 12 | } 13 | 14 | @Override 15 | public int getSamplingRate() { 16 | return samplingRate; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/com/chitek/ignition/drivers/generictcp/tests/folders/MockWriteItem.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.tests.folders; 2 | 3 | import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; 4 | import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode; 5 | import org.eclipse.milo.opcua.stack.core.types.builtin.Variant; 6 | 7 | import com.inductiveautomation.xopc.driver.api.items.WriteItem; 8 | 9 | public class MockWriteItem implements WriteItem { 10 | 11 | private String address; 12 | private Object addressObject; 13 | private Variant value; 14 | private StatusCode writeStatus; 15 | 16 | public MockWriteItem(String address, Variant value) { 17 | this.address = address; 18 | this.value = value; 19 | } 20 | 21 | @Override 22 | public String getAddress() { 23 | return address; 24 | } 25 | 26 | @Override 27 | public Object getAddressObject() { 28 | return addressObject; 29 | } 30 | 31 | @Override 32 | public NodeId getNodeId() { 33 | return null; 34 | } 35 | 36 | @Override 37 | public void setAddressObject(Object obj) { 38 | addressObject = obj; 39 | } 40 | 41 | @Override 42 | public Variant getWriteValue() { 43 | return value; 44 | } 45 | 46 | @Override 47 | public void setWriteStatus(StatusCode status) { 48 | writeStatus = status; 49 | } 50 | 51 | public StatusCode getWriteStatus() { 52 | return writeStatus; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/com/chitek/ignition/drivers/generictcp/tests/folders/TestDeviceStatusFolder.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.tests.folders; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | import static org.junit.Assert.assertEquals; 5 | import static org.junit.Assert.assertNotNull; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import org.eclipse.milo.opcua.sdk.server.nodes.UaNode; 11 | import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; 12 | import org.eclipse.milo.opcua.stack.core.types.enumerated.NodeClass; 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | 16 | import com.chitek.ignition.drivers.generictcp.folder.DeviceStatusFolder; 17 | import com.chitek.ignition.drivers.generictcp.tests.MockDriverContext; 18 | import com.inductiveautomation.xopc.driver.util.TagTree.TagTreeNode; 19 | 20 | public class TestDeviceStatusFolder { 21 | 22 | private static final String DEVICE_NAME = "DeviceName"; 23 | private MockDriverContext driverContext; 24 | 25 | @Before 26 | public void setup() throws Exception { 27 | driverContext = new MockDriverContext(DEVICE_NAME); 28 | } 29 | 30 | @Test 31 | public void testBrowseTree() throws Exception { 32 | new DeviceStatusFolder(driverContext, 1, "Device1"); 33 | 34 | TagTreeNode folderNode = driverContext.getBrowseTree().findTag("Device1"); 35 | assertNotNull(folderNode); 36 | assertNotNull(folderNode.getTag()); 37 | 38 | TagTreeNode rootNode = driverContext.getBrowseTree().findTag("Device1/[Status]"); 39 | assertNotNull(rootNode); 40 | assertNotNull(rootNode.getTag()); 41 | 42 | String[] expectedNodes = new String[]{"Device1/[Status]/Is Connected"}; 43 | List browseNodes = new ArrayList(); 44 | for (TagTreeNode childNode : rootNode.getChildren()) { 45 | // TagTreeNode.Address contains a modified address to use the TagTree with Arrays 46 | // See 'addTagToBrowseTree' 47 | // TagTreeNode.Tag contains the real address of the tag 48 | browseNodes.add(childNode.getTag()); 49 | System.out.println("BrowseNode: " + childNode.getAddress() + " Tag:" + childNode.getTag()); 50 | } 51 | assertArrayEquals(expectedNodes, browseNodes.toArray()); 52 | } 53 | 54 | @Test 55 | public void testNodeId() throws Exception { 56 | new DeviceStatusFolder(driverContext, 1, "Device1"); 57 | 58 | NodeId nodeId = new NodeId(1, String.format("[%s]%s", DEVICE_NAME, "Device1/[Status]")); 59 | UaNode folderNode = driverContext.getNode(nodeId); 60 | assertEquals(NodeClass.Object, folderNode.getNodeClass()); 61 | assertEquals("[Status]", folderNode.getBrowseName().getName()); 62 | 63 | nodeId = new NodeId(1, String.format("[%s]%s", DEVICE_NAME, "Device1/[Status]/Is Connected")); 64 | UaNode dataNode = driverContext.getNode(nodeId); 65 | assertEquals(NodeClass.Variable, dataNode.getNodeClass()); 66 | assertEquals("Is Connected", dataNode.getBrowseName().getName()); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/com/chitek/ignition/drivers/generictcp/tests/folders/TestSubscription.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.tests.folders; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import org.junit.Before; 10 | import org.junit.ClassRule; 11 | import org.junit.Test; 12 | import org.junit.rules.TemporaryFolder; 13 | 14 | import com.chitek.ignition.drivers.generictcp.folder.IndexMessageFolder; 15 | import com.chitek.ignition.drivers.generictcp.folder.MessageFolder; 16 | import com.chitek.ignition.drivers.generictcp.meta.config.DriverSettings; 17 | import com.chitek.ignition.drivers.generictcp.meta.config.MessageConfig; 18 | import com.chitek.ignition.drivers.generictcp.tests.MockDriverContext; 19 | import com.chitek.ignition.drivers.generictcp.tests.TestUtils; 20 | import com.chitek.ignition.drivers.generictcp.types.OptionalDataType; 21 | import com.inductiveautomation.ignition.common.execution.SelfSchedulingRunnable; 22 | import com.inductiveautomation.xopc.driver.api.items.SubscriptionItem; 23 | 24 | public class TestSubscription { 25 | 26 | private static final String DEVICE_NAME = "DeviceName"; 27 | 28 | private MockDriverContext driverContext; 29 | private IndexMessageFolder folder; 30 | 31 | @ClassRule 32 | public static TemporaryFolder testFolder = new TemporaryFolder(); 33 | 34 | @Before 35 | public void setup() throws Exception { 36 | driverContext = new MockDriverContext(DEVICE_NAME); 37 | driverContext.setDiskPath(testFolder.newFolder().getAbsolutePath()); 38 | 39 | // Create settings with message id type = None 40 | DriverSettings driverSettings = new DriverSettings("noHost", 0 , true, 1000, 1000, false, 1, (2^32)-1, OptionalDataType.None); 41 | MessageConfig messageConfig = TestUtils.readMessageConfig("/testMessageConfigSimple.xml"); 42 | 43 | 44 | folder = new IndexMessageFolder(messageConfig, driverSettings, 0, messageConfig.getMessageAlias(), driverContext); 45 | // Send an initial message, so we have a value 46 | folder.messageArrived(new byte[]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65,66}, null); // 65,66 == 'AB' 47 | // We have to run the scheduled command to evaluate the message 48 | driverContext.getExecutor().runCommand(); 49 | } 50 | 51 | @Test 52 | public void testSubscription() throws Exception { 53 | 54 | List items = new ArrayList(); 55 | MockSubscriptionItem subscriptionData1 = new MockSubscriptionItem("Alias1/Data1", 1000); 56 | items.add(subscriptionData1); 57 | 58 | folder.changeSubscription(items, null); 59 | 60 | SelfSchedulingRunnable subscriptionUpdater = driverContext.getSelfSchedulingRunnable(folder.getFolderAddress(), MessageFolder.UPDATER_COMMAND_NAME); 61 | assertNotNull(subscriptionUpdater); 62 | 63 | // We have to run the subscription updater to update the subscriptions 64 | subscriptionUpdater.run(); 65 | 66 | // The subscription item should have a valid value now 67 | assertEquals("AB", subscriptionData1.getValue().getValue().getValue()); 68 | } 69 | 70 | @Test 71 | public void testValueUpdates() throws Exception { 72 | 73 | List items = new ArrayList(); 74 | MockSubscriptionItem subscriptionData1 = new MockSubscriptionItem("Alias1/Data1", 1000); 75 | items.add(subscriptionData1); 76 | 77 | folder.changeSubscription(items, null); 78 | runUpdater(); 79 | assertEquals("AB", subscriptionData1.getValue().getValue().getValue()); 80 | 81 | // Send a new message 82 | folder.messageArrived(new byte[]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,67,68}, null); // 675,68 == 'CD' 83 | // We have to run the scheduled command to evaluate the message 84 | driverContext.getExecutor().runCommand(); 85 | runUpdater(); 86 | assertEquals("CD", subscriptionData1.getValue().getValue().getValue()); 87 | } 88 | 89 | /** 90 | * @return 91 | * The scheduled execution rate 92 | */ 93 | private long runUpdater() { 94 | SelfSchedulingRunnable subscriptionUpdater = driverContext.getSelfSchedulingRunnable(folder.getFolderAddress(), MessageFolder.UPDATER_COMMAND_NAME); 95 | 96 | // We have to run the subscription updater to update the subscriptions 97 | subscriptionUpdater.run(); 98 | // The updater has to run twice, first run updates data items, second run updates special items 99 | subscriptionUpdater.run(); 100 | return subscriptionUpdater.getNextExecDelayMillis(); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/test/java/com/chitek/ignition/drivers/generictcp/tests/io/TestTimeoutHandler.java: -------------------------------------------------------------------------------- 1 | package com.chitek.ignition.drivers.generictcp.tests.io; 2 | 3 | import static org.hamcrest.core.Is.is; 4 | import static org.hamcrest.number.OrderingComparison.lessThanOrEqualTo; 5 | import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo; 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.assertFalse; 8 | import static org.junit.Assert.assertThat; 9 | import static org.junit.Assert.assertTrue; 10 | 11 | import java.net.InetAddress; 12 | import java.net.InetSocketAddress; 13 | 14 | import org.junit.Test; 15 | 16 | import com.chitek.ignition.drivers.generictcp.io.TimeoutHandler; 17 | 18 | public class TestTimeoutHandler { 19 | 20 | @Test 21 | public void testTimeout() throws Exception { 22 | TimeoutHandler handler = new TimeoutHandler(10); 23 | 24 | assertEquals(10, handler.getTimeToTimeout()); 25 | assertEquals(null, handler.getTimeoutAddress()); 26 | 27 | InetSocketAddress addr1 = new InetSocketAddress(InetAddress.getByAddress(new byte[]{(byte) 192,(byte) 168,0,1}),9999); 28 | InetSocketAddress addr2 = new InetSocketAddress(InetAddress.getByAddress(new byte[]{(byte) 192,(byte) 168,0,2}),9998); 29 | 30 | handler.dataReceived(addr1); 31 | assertThat(handler.getTimeToTimeout(), is(greaterThanOrEqualTo(9L))); 32 | assertEquals(addr1, handler.getTimeoutAddress()); 33 | assertFalse(handler.isTimeoutExpired()); 34 | 35 | handler.dataReceived(addr2); 36 | assertEquals(addr1, handler.getTimeoutAddress()); 37 | 38 | handler.removeAddress(addr1); 39 | assertEquals(addr2, handler.getTimeoutAddress()); 40 | 41 | long timeout=handler.getTimeToTimeout(); 42 | assertThat(timeout, is(lessThanOrEqualTo(10L))); 43 | 44 | Thread.sleep(5); 45 | assertThat(handler.getTimeToTimeout(), is(lessThanOrEqualTo(timeout-4L))); 46 | 47 | Thread.sleep(5); 48 | assertTrue(handler.isTimeoutExpired()); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=DEBUG, ConsoleAppender 2 | 3 | log4j.appender.ConsoleAppender=org.apache.log4j.ConsoleAppender 4 | log4j.appender.ConsoleAppender.layout=org.apache.log4j.PatternLayout 5 | log4j.appender.ConsoleAppender.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c: %m%n -------------------------------------------------------------------------------- /src/test/resources/testHeaderConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | PacketSize 4 | 5 | 1 6 | 7 | 8 | Word 9 | 0xff 10 | 1 11 | 12 | true 13 | true 14 | false 15 | 16 | -------------------------------------------------------------------------------- /src/test/resources/testHeaderConfigByte.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | PacketSize 4 | 5 | 1 6 | 7 | 8 | Byte 9 | 160 10 | 1 11 | 12 | true 13 | true 14 | false 15 | 16 | -------------------------------------------------------------------------------- /src/test/resources/testHeaderConfigHandshake.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | PacketSize 4 | 5 | 1 6 | 7 | 8 | Word 9 | 0xff 10 | 1 11 | 12 | true 13 | true 14 | true 15 | lenw, 0xff,0xfe 16 | -------------------------------------------------------------------------------- /src/test/resources/testHeaderConfigSimple.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | PacketSize 4 | 5 | 1 6 | 7 | true 8 | false 9 | false 10 | 11 | -------------------------------------------------------------------------------- /src/test/resources/testMessageConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Data1 4 | 1 5 | Int16 6 | 1 7 | 8 | 9 | Data2 10 | 2 11 | Int16 12 | 1 13 | 14 | Alias1 15 | 1 16 | false 17 | NONE 18 | -------------------------------------------------------------------------------- /src/test/resources/testMessageConfigPacketBased.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Data1 4 | 1 5 | Int16 6 | 1 7 | 8 | 9 | Data2 10 | 2 11 | String 12 | 3 13 | PACKET_BASED 14 | 15 | PACKET_BASED 16 | Alias1 17 | 1 18 | false 19 | NONE 20 | -------------------------------------------------------------------------------- /src/test/resources/testMessageConfigPersistant.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Data1 4 | 1 5 | String 6 | 2 7 | 8 | Alias1 9 | 0 10 | true 11 | HANDSHAKE 12 | -------------------------------------------------------------------------------- /src/test/resources/testMessageConfigQueue.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Data1 4 | 1 5 | String 6 | 2 7 | 8 | Alias1 9 | 0 10 | false 11 | HANDSHAKE 12 | -------------------------------------------------------------------------------- /src/test/resources/testMessageConfigSimple.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Data1 4 | 1 5 | String 6 | 2 7 | 8 | Alias1 9 | 0 10 | false 11 | NONE 12 | -------------------------------------------------------------------------------- /src/test/resources/testMessageConfigTypes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Data1 4 | 1 5 | String 6 | 5 7 | 8 | 9 | Data2 10 | 2 11 | RawString 12 | 5 13 | 14 | 15 | Data3 16 | 3 17 | Bool16 18 | 1 19 | 20 | Alias1 21 | 1 22 | false 23 | NONE 24 | -------------------------------------------------------------------------------- /src/test/resources/testMessageConfigWithAge.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Data1 4 | 1 5 | Int16 6 | 1 7 | 8 | 9 | Data2 10 | 2 11 | Int16 12 | 1 13 | 14 | 15 | null 16 | 0 17 | MessageAge 18 | 1 19 | 20 | Alias1 21 | 1 22 | false 23 | NONE 24 | -------------------------------------------------------------------------------- /src/test/resources/testWritebackConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | true 3 | UByte 4 | false 5 | true 6 | ByteString 7 | 0x38,0x39 8 | 99 9 | true 10 | lenw,0xff,0xff, id 11 | --------------------------------------------------------------------------------