From 4a13e33ce34c8fa013bf52b081734b06306a4bdf Mon Sep 17 00:00:00 2001
From: Alex Biddle
Date: Mon, 20 Jan 2020 11:57:11 +0000
Subject: [PATCH] Apply upstream patch
Author: darkweb
Date: Thu Nov 7 21:50:37 2019 +0000
Update the open source code of the third party product "ModbusPal" to have optional command line arguments for the executable JAR:
1) -loadFile - Loads a file at launch. Provide the absolute path.
2) -portNumber - An IP port number to connect this current configuration.
3) -help - A command line help message to display the command line arguments.
git-svn-id: https://svn.code.sf.net/p/modbuspal/code@130 42ed8c19-e739-47c8-872a-484d4b446698
---
.../modbuspal/automation/AutomationPanel.java | 4 +-
.../modbuspal/help/modbus-slaves-add-tcp.html | 8 +-
.../java/modbuspal/main/ModbusPalGui.java | 78 +++++++++++++++---
.../java/modbuspal/main/ModbusPalPane.java | 79 +++++++++++++++++--
.../java/modbuspal/main/ModbusPalProject.java | 63 +++++++++++----
.../java/modbuspal/slave/ModbusSlave.java | 13 ++-
src/main/java/modbuspal/toolkit/XMLTools.java | 4 +
7 files changed, 210 insertions(+), 39 deletions(-)
diff --git a/src/main/java/modbuspal/automation/AutomationPanel.java b/src/main/java/modbuspal/automation/AutomationPanel.java
index 3e88ccb..f70f69b 100644
--- a/src/main/java/modbuspal/automation/AutomationPanel.java
+++ b/src/main/java/modbuspal/automation/AutomationPanel.java
@@ -279,20 +279,20 @@ public void windowDeactivated(WindowEvent e)
{
}
- @Override
public void automationHasEnded(Automation source)
{
//playToggleButton.setText("Start");
playToggleButton.setSelected(false);
setBackground(false);
+ automation.stop();
}
- @Override
public void automationHasStarted(Automation aThis)
{
//playToggleButton.setText("Stop");
playToggleButton.setSelected(true);
setBackground(true);
+ automation.start();
}
@Override
diff --git a/src/main/java/modbuspal/help/modbus-slaves-add-tcp.html b/src/main/java/modbuspal/help/modbus-slaves-add-tcp.html
index bf5d491..d65f020 100644
--- a/src/main/java/modbuspal/help/modbus-slaves-add-tcp.html
+++ b/src/main/java/modbuspal/help/modbus-slaves-add-tcp.html
@@ -28,11 +28,11 @@ Regular MODBUS TCP addressing
192.168.10.100
Create just one slave that will only reply to requests received
on the network interface whose IP address is 192.168.10.100.
- 10.23.6.2, 10.23.6.8
+ 10.23.6.2, 10.23.6.8 (not implemented)
Create 2 slaves that will reply to requests received
on network interfaces whose IP addresses are 10.23.6.2,
and 10.23.6.8.
- 10.23.6.2-10.23.6.8
+ 10.23.6.2-10.23.6.8 (not implemented)
Create 9 slaves that will reply to requests received
on network interfaces whose IP addresses are 10.23.6.2,
10.23.6.3, ..., 10.23.6.7 and 10.23.6.8.
@@ -53,10 +53,10 @@ Multiple slaves sharing the same IP address
following patterns:
- 127.0.0.1(17)
+ 127.0.0.1(17) (not implemented)
Create juste one slave at IP address 127.0.0.1 (listen on all
network interfaces) and slave number 17.
- 192.168.10.100(10-15)
+ 192.168.10.100(10-15) (not implemented)
Create 6 slaves sharing the same IP address (local network interface
192.168.10.100). Each slave is assigned a slave number ranging from 10
to 15 included.
diff --git a/src/main/java/modbuspal/main/ModbusPalGui.java b/src/main/java/modbuspal/main/ModbusPalGui.java
index 3bb567a..b9e2d65 100644
--- a/src/main/java/modbuspal/main/ModbusPalGui.java
+++ b/src/main/java/modbuspal/main/ModbusPalGui.java
@@ -30,9 +30,56 @@ public class ModbusPalGui
{
private static final HashMap instances = new HashMap();
+ public static final int MAX_PORT_NUMBER = 65536;
+
+ private static String initialLoadFilePath = "";
+ private static int initialPortNumber = -1;
+
+ /**
+ * This method will display the help message to the console and exit the software.
+ */
+ public static void displayHelpMessage()
+ {
+ System.out.println( "This software launches the Modbus Slave simulation program: ModbusPal." );
+ System.out.println( "Arguments in this program include:" );
+ System.out.println(
+ "-install (optional): This flag is to tell program to install itself if it is not installed." );
+ System.out.println( "-loadFile (optional): This argument is to load a project file at launch. Example usage:" );
+ System.out.println( "\t-loadFile=\"your/absolute/path/name/example.xmpp\"" );
+ System.out.println( "Make sure the path name is the absolute path, or you will get an error message back." );
+ System.out.println(
+ "-portNumber (optional): This argument sets the initial TCP/IP port number to a number between 0 and "
+ + MAX_PORT_NUMBER + ". Example usage:" );
+ System.out.println( "\t-portNumber=1234" );
+ System.out.println(
+ "Make sure the port you choose is not a reserved port number or in use. If a number is not given, an error message will be returned." );
+ System.out.println(
+ "If this argument is not given or an invalid port value is given, then the port number will be set to "
+ + ModbusPalPane.DEFAULT_PORT_TEXT + ", or the value in the initial project file loaded." );
+ System.out.println( "-help (optional): Displays the help message." );
+ System.exit( 0 );
+ }
+
+ /**
+ * This method gets the initial load file path to load specified by the user in the command line arguments.
+ * @return {String} The absolute initial load file path. Returns "" if no argument was given.
+ */
+ public static String getInitialLoadFilePath()
+ {
+ return initialLoadFilePath;
+ }
+
+ /**
+ * This method gets the initial port number to load specified by the user in the command line arguments.
+ * @return {int} The initial port number for TCP/IP connections. Returns -1 if no port number was given.
+ */
+ public static int getInitialPortNumber()
+ {
+ return initialPortNumber;
+ }
/**
- * this method will try to change the Look and Feel of the applcation,
+ * this method will try to change the Look and Feel of the application,
* using the system l&f. It means that the application will get the Windows
* l&f on Windows, etc...
*/
@@ -55,31 +102,48 @@ public static void install()
}
/**
- * @param args the command line arguments
+ * @param {String[]} args The command line arguments
*/
public static void main(String args[])
{
boolean runInstall = false;
boolean runGui = true;
+ String installArgFlag = "-install";
+ String loadFileArgFlag = "-loadFile=";
+ String portNumberArgFlag = "-portNumber=";
- if( args.length>=1 )
+ if( args.length >= 1 )
{
for(String arg:args)
{
- if( arg.compareToIgnoreCase("-install")==0 )
+ if( arg.startsWith( installArgFlag ) )
{
runInstall = true;
runGui = false;
}
+ else if( arg.startsWith( loadFileArgFlag ) )
+ {
+ initialLoadFilePath = arg.substring( arg.lastIndexOf( loadFileArgFlag ) + loadFileArgFlag.length() );
+ }
+ else if( arg.startsWith( portNumberArgFlag ) )
+ {
+ String portNumberString = arg.substring( arg.lastIndexOf( portNumberArgFlag ) + portNumberArgFlag.length() );
+ initialPortNumber = Integer.valueOf( portNumberString ).intValue();
+ }
+ else
+ {
+ displayHelpMessage();
+ }
}
}
+
if( runInstall == true )
{
install();
}
if( runGui == true )
- {
+ {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
@@ -88,12 +152,8 @@ public void run() {
}
});
}
-
-
}
-
-
/**
* A JinternalFrame that contains a ModbusPalPane.
*/
diff --git a/src/main/java/modbuspal/main/ModbusPalPane.java b/src/main/java/modbuspal/main/ModbusPalPane.java
index cb5a4ce..9938864 100644
--- a/src/main/java/modbuspal/main/ModbusPalPane.java
+++ b/src/main/java/modbuspal/main/ModbusPalPane.java
@@ -53,6 +53,9 @@ public class ModbusPalPane
/** Base registry key for the configuration of the application. */
public static final String BASE_REGISTRY_KEY = "modbuspal";
+
+ /** Default TCP/IP port in a string to be loaded into the GUI. */
+ public static final String DEFAULT_PORT_TEXT = "502";
private ArrayList listeners = new ArrayList();
@@ -284,12 +287,66 @@ public ModbusPalPane(boolean useInternalConsole)
installRecorder();
installCommPorts();
//installScriptEngine();
-
- setProject( new ModbusPalProject() );
-
+
+ String initialLoadProjectFilePath = ModbusPalGui.getInitialLoadFilePath();
+ ModbusPalProject project = null;
+ File fileCheck = new File( initialLoadProjectFilePath );
+ if( initialLoadProjectFilePath != "" && fileCheck.isFile() )
+ {
+ try
+ {
+ System.out.println( "Loading the project file: " + initialLoadProjectFilePath );
+ project = loadProject( initialLoadProjectFilePath );
+
+ // Need to initialize the port number after loading the project, and not every time we load or set a project because if the
+ // user wants to load another file, we don't want to clobber it with the command line initial port number.
+ // This call must be called before the runToggleButton.doClick() method is invoked.
+ initializeInitialPortNumber( project );
+
+ // Now that we have loaded a project from an initial project file, it is time to start all of the
+ // automations that have been loaded from the project file.
+ Component panels[] = automationsListPanel.getComponents();
+ for( int panelIndex = 0; panelIndex < panels.length; panelIndex++ )
+ {
+ if( panels[ panelIndex ] instanceof AutomationPanel )
+ {
+ AutomationPanel panel = ( AutomationPanel ) panels[ panelIndex ];
+ panel.automationHasStarted( null );
+ }
+ }
+ runToggleButton.doClick();
+ }
+ catch( Exception exception )
+ {
+ System.out.println(
+ "Could not load the initial project file path \"" + initialLoadProjectFilePath + "\"." );
+ System.out.println( "Check the path you inputted into the command line arguments." );
+ }
+ }
+ else
+ {
+ project = new ModbusPalProject();
+ setProject( project );
+ // Initializing the initial port number in case a port number was given, but no initial load file was given in the
+ // command line arguments.
+ initializeInitialPortNumber( project );
+ }
}
-
+ /**
+ * Initialize the initial port number given from the command line arguments if there was a valid number given in the command line.
+ * @param project The current ModbusPal project that is being used.
+ */
+ private void initializeInitialPortNumber( ModbusPalProject project )
+ {
+ int initialPortNumber = ModbusPalGui.getInitialPortNumber();
+ if( initialPortNumber >= 0 && initialPortNumber <= ModbusPalGui.MAX_PORT_NUMBER )
+ {
+ project.linkTcpipPort= Integer.toString( initialPortNumber );
+ }
+ portTextField.setText( project.linkTcpipPort );
+ }
+
private void installConsole()
{
try {
@@ -501,8 +558,19 @@ private void initComponents() {
gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 2);
tcpIpSettingsPanel.add(jLabel1, gridBagConstraints);
- portTextField.setText("502");
+ int initialPortNumber = ModbusPalGui.getInitialPortNumber();
+ if( initialPortNumber >= 0 && initialPortNumber <= ModbusPalGui.MAX_PORT_NUMBER )
+ {
+ System.out.println( "Loading the initial TCP/IP port number: " + initialPortNumber );
+ portTextField.setText( Integer.toString( initialPortNumber ) );
+ }
+ else
+ {
+ System.out.println( "Could not load an initial port number. Loading default port number." );
+ portTextField.setText( DEFAULT_PORT_TEXT );
+ }
portTextField.setPreferredSize(new java.awt.Dimension(40, 20));
+
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.insets = new java.awt.Insets(5, 2, 5, 5);
tcpIpSettingsPanel.add(portTextField, gridBagConstraints);
@@ -1314,7 +1382,6 @@ private void loadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FI
private void addAutomationButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addAutomationButtonActionPerformed
-
String name = Automation.DEFAULT_NAME + " #" + String.valueOf( modbusPalProject.idGenerator.createID() );
Automation automation = new Automation( name );
modbusPalProject.addAutomation(automation);
diff --git a/src/main/java/modbuspal/main/ModbusPalProject.java b/src/main/java/modbuspal/main/ModbusPalProject.java
index 82118f6..2dcfdb8 100644
--- a/src/main/java/modbuspal/main/ModbusPalProject.java
+++ b/src/main/java/modbuspal/main/ModbusPalProject.java
@@ -10,6 +10,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
@@ -109,8 +111,6 @@ public ModbusPalProject()
generatorFactory.add( new modbuspal.generator.linear.LinearGenerator() );
generatorFactory.add( new modbuspal.generator.random.RandomGenerator() );
generatorFactory.add( new modbuspal.generator.sine.SineGenerator() );
-
-
}
private ModbusPalProject(Document doc, File source)
@@ -411,8 +411,11 @@ private void loadBindings(Document doc, ModbusSlave slave)
/**
* This method will examine the content of a "" tag in order to
* parse the attributes it contains, and also the child tags that may exist.
- * @param node reference on the Node that represents a "" tag in the
+ * @param node Reference on the Node that represents a "" tag in the
* project.
+ * @param slave Reference to a ModbusSlave to bind the automation to. If the passed in
+ * slave is "null", it will retrieve the ModbusSlave that is the parent of this register or coil
+ * node the automation is being bound to.
* @throws java.lang.InstantiationException
* @throws java.lang.IllegalAccessException
*/
@@ -443,21 +446,36 @@ private void loadBinding(Node node, ModbusSlave slave)
Node orderNode = attributes.getNamedItem("order");
String orderValue = orderNode.getNodeValue();
int wordOrder = Integer.parseInt(orderValue);
+
+ boolean isRegister = true;
// retrieve the register that is the parent of this node
- Node parentRegister = XMLTools.findParent(node,"register");
- String parentAddress = XMLTools.getAttribute(ModbusPalXML.XML_ADDRESS_ATTRIBUTE, parentRegister);
- int registerAddress = Integer.parseInt( parentAddress );
+ Node parentNode = XMLTools.findParent(node,"register");
+ String parentAddress = XMLTools.getAttribute(ModbusPalXML.XML_ADDRESS_ATTRIBUTE, parentNode);
+
+ int ioAddress = 0;
+ if( parentAddress == null )
+ {
+ isRegister = false;
+ parentNode = XMLTools.findParent(node, "coil");
+ parentAddress = XMLTools.getAttribute(ModbusPalXML.XML_ADDRESS_ATTRIBUTE, parentNode);
+ if( parentAddress == null )
+ {
+ System.out.println( "Cannot bind automation. Parent is neither a register or coil!" );
+ return;
+ }
+ }
+
+ ioAddress = Integer.parseInt( parentAddress );
- // Instanciate the binding:
+ // Instantiate the binding:
Binding binding = bindingFactory.newInstance(className);
binding.setup(automation, wordOrder);
-
- if( slave==null)
+ if( slave==null )
{
// retrieve the slave that is the parent of this register
- Node parentSlave = XMLTools.findParent(parentRegister, "slave");
+ Node parentSlave = XMLTools.findParent(parentNode, "slave");
String slaveAddress = XMLTools.getAttribute(ModbusPalXML.XML_SLAVE_ID2_ATTRIBUTE, parentSlave);
if( slaveAddress!= null )
@@ -467,15 +485,28 @@ private void loadBinding(Node node, ModbusSlave slave)
}
else
{
- slaveAddress = XMLTools.getAttribute(ModbusPalXML.XML_SLAVE_ID_ATTRIBUTE, parentSlave);
- int slaveId = Integer.parseInt(slaveAddress);
- ModbusSlaveAddress msa = new ModbusSlaveAddress(slaveId);
- slave = getModbusSlave(msa);
+ slaveAddress = XMLTools.getAttribute(ModbusPalXML.XML_SLAVE_ID_ATTRIBUTE, parentSlave);
+ try
+ {
+ ModbusSlaveAddress msa = new ModbusSlaveAddress( InetAddress.getByName( slaveAddress ) );
+ slave = getModbusSlave(msa);
+ }
+ catch (UnknownHostException exception)
+ {
+ System.out.println( "Unable to get Modbus Slave IP address from slave address: " + slaveAddress );
+ }
}
}
- // bind the register and the automation
- slave.getHoldingRegisters().bind(registerAddress, binding);
+ // bind the registers, coils, and the automation
+ if( isRegister )
+ {
+ slave.getHoldingRegisters().bind(ioAddress, binding);
+ }
+ else
+ {
+ slave.getCoils().bind(ioAddress, binding);
+ }
}
diff --git a/src/main/java/modbuspal/slave/ModbusSlave.java b/src/main/java/modbuspal/slave/ModbusSlave.java
index 9dc53ee..64855c4 100644
--- a/src/main/java/modbuspal/slave/ModbusSlave.java
+++ b/src/main/java/modbuspal/slave/ModbusSlave.java
@@ -7,6 +7,8 @@
import java.io.IOException;
import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -630,8 +632,15 @@ public void load(ModbusPalProject mpp, Node node, boolean importMode)
}
else
{
- String id = XMLTools.getAttribute(XML_SLAVE_ID_ATTRIBUTE, node);
- slaveId = new ModbusSlaveAddress(Integer.valueOf(id));
+ String id = XMLTools.getAttribute(XML_SLAVE_ID_ATTRIBUTE, node);
+ try
+ {
+ slaveId = new ModbusSlaveAddress( InetAddress.getByName( id ) );
+ }
+ catch (UnknownHostException exception)
+ {
+ System.out.println( "Unknown host while loading Modbus Slave: " + id );
+ }
}
String en = XMLTools.getAttribute(XML_SLAVE_ENABLED_ATTRIBUTE, node);
diff --git a/src/main/java/modbuspal/toolkit/XMLTools.java b/src/main/java/modbuspal/toolkit/XMLTools.java
index 801baa5..5c5c70d 100644
--- a/src/main/java/modbuspal/toolkit/XMLTools.java
+++ b/src/main/java/modbuspal/toolkit/XMLTools.java
@@ -71,6 +71,10 @@ public InputSource resolveEntity(String publicId, String systemId)
*/
public static String getAttribute(String attr, Node node)
{
+ if( node == null )
+ {
+ return null;
+ }
NamedNodeMap attributes = node.getAttributes();
Node attribute = attributes.getNamedItem(attr);
if( attribute==null )