/*
|
* Copyright 2011-17 Fraunhofer ISE, energy & meteo Systems GmbH and other contributors
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*
|
*/
|
package org.openmuc.openiec61850.clientgui;
|
|
import java.awt.Component;
|
import java.awt.Dimension;
|
import java.awt.GridBagConstraints;
|
import java.awt.GridBagLayout;
|
import java.awt.Insets;
|
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionListener;
|
import java.io.FileInputStream;
|
import java.io.FileOutputStream;
|
import java.io.IOException;
|
import java.io.InputStream;
|
import java.net.InetAddress;
|
import java.net.UnknownHostException;
|
import java.util.Properties;
|
|
import javax.swing.Box;
|
import javax.swing.BoxLayout;
|
import javax.swing.JButton;
|
import javax.swing.JFrame;
|
import javax.swing.JLabel;
|
import javax.swing.JPanel;
|
import javax.swing.JScrollPane;
|
import javax.swing.JSeparator;
|
import javax.swing.JTextField;
|
import javax.swing.JTree;
|
import javax.swing.ToolTipManager;
|
import javax.swing.UIManager;
|
import javax.swing.UnsupportedLookAndFeelException;
|
import javax.swing.event.TreeSelectionEvent;
|
import javax.swing.event.TreeSelectionListener;
|
import javax.swing.tree.DefaultMutableTreeNode;
|
import javax.swing.tree.DefaultTreeModel;
|
|
import org.openmuc.openiec61850.ClientAssociation;
|
import org.openmuc.openiec61850.ClientSap;
|
import org.openmuc.openiec61850.ServerModel;
|
import org.openmuc.openiec61850.ServiceError;
|
import org.openmuc.openiec61850.clientgui.util.Counter;
|
|
public class ClientGui extends JFrame implements ActionListener, TreeSelectionListener {
|
|
private static final String ADDRESS_KEY = "serverAddress";
|
private static final String PORT_KEY = "serverPort";
|
private static final String TSEL_LOCAL_KEY = "tselLocal";
|
private static final String TSEL_REMOTE_KEY = "tselRemote";
|
private static final String LASTCONNECTION_FILE = "lastconnection.properties";
|
|
private static final long serialVersionUID = -1938913902977758367L;
|
|
private final JTextField ipTextField = new JTextField("127.0.0.1");
|
private final JTextField portTextField = new JTextField("10002");
|
private final JTree tree = new JTree(new DefaultMutableTreeNode("No server connected"));
|
private final JPanel detailsPanel = new JPanel();
|
private final GridBagLayout detailsLayout = new GridBagLayout();
|
|
private final SettingsFrame settingsFrame = new SettingsFrame();
|
|
private ClientAssociation association;
|
|
private DataTreeNode selectedNode;
|
|
public ClientGui() {
|
super("WHYC IEC61850 Client");
|
|
Properties lastConnection = new Properties();
|
|
InputStream in = null;
|
try {
|
in = new FileInputStream(LASTCONNECTION_FILE);
|
lastConnection.load(in);
|
|
ipTextField.setText(lastConnection.getProperty(ADDRESS_KEY));
|
portTextField.setText(lastConnection.getProperty(PORT_KEY));
|
|
String[] tselString = lastConnection.getProperty(TSEL_LOCAL_KEY).split(",");
|
byte[] tsel = new byte[] { (byte) Integer.parseInt(tselString[0]), (byte) Integer.parseInt(tselString[1]) };
|
settingsFrame.setTselLocal(tsel);
|
|
tselString = lastConnection.getProperty(TSEL_REMOTE_KEY).split(",");
|
tsel = new byte[] { (byte) Integer.parseInt(tselString[0]), (byte) Integer.parseInt(tselString[1]) };
|
settingsFrame.setTselRemote(tsel);
|
} catch (Exception ex) {
|
// no lastconnection.properties file found, use default.
|
} finally {
|
try {
|
if (in != null) {
|
in.close();
|
}
|
} catch (IOException e) {
|
e.printStackTrace();
|
|
}
|
}
|
|
try {
|
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
} catch (ClassNotFoundException e) {
|
System.out.println("Class not found: " + e.getMessage());
|
} catch (InstantiationException e) {
|
System.out.println("Object not instantiated: " + e.getMessage());
|
} catch (IllegalAccessException e) {
|
System.out.println("Illegal acces: " + e.getMessage());
|
} catch (UnsupportedLookAndFeelException e) {
|
System.out.println("Unsupported LookAndFeel: " + e.getMessage());
|
}
|
|
GridBagLayout gbl = new GridBagLayout();
|
setLayout(gbl);
|
|
JPanel topPanel = new JPanel();
|
topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.X_AXIS));
|
|
GridBagConstraints topPanelConstraint = new GridBagConstraints();
|
topPanelConstraint.fill = GridBagConstraints.HORIZONTAL;
|
topPanelConstraint.gridwidth = GridBagConstraints.REMAINDER;
|
topPanelConstraint.gridx = 0;
|
topPanelConstraint.gridy = 0;
|
topPanelConstraint.insets = new Insets(5, 5, 5, 5);
|
topPanelConstraint.anchor = GridBagConstraints.NORTH;
|
gbl.setConstraints(topPanel, topPanelConstraint);
|
add(topPanel);
|
|
JLabel label = new JLabel("IP: ");
|
topPanel.add(label);
|
topPanel.add(ipTextField);
|
topPanel.add(Box.createRigidArea(new Dimension(5, 0)));
|
|
label = new JLabel("Port: ");
|
topPanel.add(label);
|
topPanel.add(portTextField);
|
topPanel.add(Box.createRigidArea(new Dimension(5, 0)));
|
|
JButton newServerButton = new JButton("Connect to Server");
|
newServerButton.addActionListener(this);
|
newServerButton.setActionCommand("Connect");
|
topPanel.add(newServerButton);
|
topPanel.add(Box.createRigidArea(new Dimension(5, 0)));
|
|
JButton settingsButton = new JButton("Settings");
|
settingsButton.addActionListener(this);
|
settingsButton.setActionCommand("Settings");
|
topPanel.add(settingsButton);
|
|
ToolTipManager.sharedInstance().registerComponent(tree);
|
|
tree.setCellRenderer(new DataObjectTreeCellRenderer());
|
tree.setMinimumSize(new Dimension(100, 0));
|
tree.addTreeSelectionListener(this);
|
JScrollPane treeScrollPane = new JScrollPane(tree);
|
treeScrollPane.setMinimumSize(new Dimension(100, 0));
|
treeScrollPane.setVisible(true);
|
|
GridBagConstraints treeScrollPaneConstraint = new GridBagConstraints();
|
treeScrollPaneConstraint.fill = GridBagConstraints.BOTH;
|
treeScrollPaneConstraint.gridx = 0;
|
treeScrollPaneConstraint.gridy = 1;
|
treeScrollPaneConstraint.weightx = 0.2;
|
treeScrollPaneConstraint.weighty = 1;
|
treeScrollPaneConstraint.insets = new Insets(5, 5, 5, 5);
|
gbl.setConstraints(treeScrollPane, treeScrollPaneConstraint);
|
add(treeScrollPane);
|
|
detailsPanel.setLayout(detailsLayout);
|
detailsPanel.setAlignmentY(TOP_ALIGNMENT);
|
JScrollPane detailsScrollPane = new JScrollPane(detailsPanel);
|
detailsPanel.setMaximumSize(detailsScrollPane.getSize());
|
detailsScrollPane.setMinimumSize(new Dimension(0, 0));
|
detailsScrollPane.setPreferredSize(new Dimension(200, 0));
|
detailsScrollPane.setVisible(true);
|
GridBagConstraints detailsScrollPaneConstraint = new GridBagConstraints();
|
detailsScrollPaneConstraint.fill = GridBagConstraints.BOTH;
|
detailsScrollPaneConstraint.gridx = 1;
|
detailsScrollPaneConstraint.gridy = 1;
|
detailsScrollPaneConstraint.weightx = 0.8;
|
detailsScrollPaneConstraint.weighty = 1;
|
detailsScrollPaneConstraint.insets = new Insets(5, 5, 5, 5);
|
gbl.setConstraints(detailsScrollPane, detailsScrollPaneConstraint);
|
add(detailsScrollPane);
|
|
// Display the window.
|
setSize(700, 500);
|
setMinimumSize(new Dimension(420, 0));
|
setVisible(true);
|
}
|
|
public static void main(String[] args) {
|
ClientGui clientGui = new ClientGui();
|
clientGui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
clientGui.setVisible(true);
|
}
|
|
@Override
|
public void actionPerformed(ActionEvent arg0) {
|
if ("connect".equalsIgnoreCase(arg0.getActionCommand())) {
|
connect();
|
}
|
else if ("reload".equalsIgnoreCase(arg0.getActionCommand())) {
|
reload();
|
}
|
else if ("write".equalsIgnoreCase(arg0.getActionCommand())) {
|
write();
|
}
|
else if ("settings".equalsIgnoreCase(arg0.getActionCommand())) {
|
settingsFrame.setVisible(true);
|
}
|
}
|
|
@Override
|
public void valueChanged(TreeSelectionEvent e) {
|
detailsPanel.removeAll();
|
detailsPanel.repaint();
|
if (e.getNewLeadSelectionPath() != null) {
|
selectedNode = (DataTreeNode) e.getNewLeadSelectionPath().getLastPathComponent();
|
if (selectedNode.readable()) {
|
showDataDetails(selectedNode, new Counter());
|
|
JPanel filler = new JPanel();
|
GridBagConstraints gbc = new GridBagConstraints();
|
gbc.fill = GridBagConstraints.BOTH;
|
gbc.gridx = 0;
|
gbc.gridy = GridBagConstraints.RELATIVE;
|
gbc.gridwidth = 3;
|
gbc.gridheight = 1;
|
gbc.weightx = 0;
|
gbc.weighty = 1;
|
detailsLayout.setConstraints(filler, gbc);
|
detailsPanel.add(filler);
|
|
JButton button = new JButton("Reload values");
|
button.addActionListener(this);
|
button.setActionCommand("reload");
|
gbc = new GridBagConstraints();
|
gbc.fill = GridBagConstraints.NONE;
|
gbc.gridx = 0;
|
gbc.gridy = GridBagConstraints.RELATIVE;
|
gbc.gridwidth = 2;
|
gbc.gridheight = 1;
|
gbc.weightx = 0;
|
gbc.weighty = 0;
|
gbc.anchor = GridBagConstraints.SOUTHWEST;
|
gbc.insets = new Insets(0, 5, 5, 0);
|
detailsLayout.setConstraints(button, gbc);
|
detailsPanel.add(button);
|
|
if (selectedNode.writable()) {
|
button = new JButton("Write values");
|
button.addActionListener(this);
|
button.setActionCommand("write");
|
gbc = new GridBagConstraints();
|
gbc.fill = GridBagConstraints.NONE;
|
gbc.gridx = 2;
|
gbc.gridy = GridBagConstraints.RELATIVE;
|
gbc.gridwidth = 1;
|
gbc.gridheight = 1;
|
gbc.weightx = 0;
|
gbc.weighty = 0;
|
gbc.anchor = GridBagConstraints.SOUTHEAST;
|
gbc.insets = new Insets(0, 0, 5, 5);
|
detailsLayout.setConstraints(button, gbc);
|
detailsPanel.add(button);
|
}
|
}
|
}
|
else {
|
selectedNode = null;
|
}
|
|
validate();
|
}
|
|
private void connect() {
|
ClientSap clientSap = new ClientSap();
|
|
InetAddress address = null;
|
try {
|
address = InetAddress.getByName(ipTextField.getText());
|
} catch (UnknownHostException e1) {
|
e1.printStackTrace();
|
return;
|
}
|
|
int remotePort = 10002;
|
try {
|
remotePort = Integer.parseInt(portTextField.getText());
|
if (remotePort < 1 || remotePort > 0xFFFF) {
|
throw new NumberFormatException("port must be in range [1, 65535]");
|
}
|
} catch (NumberFormatException e) {
|
e.printStackTrace();
|
return;
|
}
|
|
clientSap.setTSelLocal(settingsFrame.getTselLocal());
|
clientSap.setTSelRemote(settingsFrame.getTselRemote());
|
|
try {
|
association = clientSap.associate(address, remotePort, null, null);
|
} catch (IOException e) {
|
System.out.println("Error connecting to server: " + e.getMessage());
|
return;
|
}
|
|
ServerModel serverModel;
|
try {
|
serverModel = association.retrieveModel();
|
association.getAllDataValues();
|
} catch (ServiceError e) {
|
System.out.println("Service Error requesting model." + e.getMessage());
|
association.close();
|
return;
|
} catch (IOException e) {
|
System.out.println("Fatal IOException requesting model." + e.getMessage());
|
return;
|
}
|
|
ServerModelParser parser = new ServerModelParser(serverModel);
|
tree.setModel(new DefaultTreeModel(parser.getModelTree()));
|
|
Properties lastConnectSettings = new Properties();
|
FileOutputStream out = null;
|
try {
|
lastConnectSettings.setProperty(ADDRESS_KEY, ipTextField.getText());
|
lastConnectSettings.setProperty(PORT_KEY, portTextField.getText());
|
byte[] tsel = settingsFrame.getTselLocal();
|
lastConnectSettings.setProperty(TSEL_LOCAL_KEY, tsel[0] + "," + tsel[1]);
|
tsel = settingsFrame.getTselRemote();
|
lastConnectSettings.setProperty(TSEL_REMOTE_KEY, tsel[0] + "," + tsel[1]);
|
|
out = new FileOutputStream(LASTCONNECTION_FILE);
|
lastConnectSettings.store(out, null);
|
} catch (IOException ex) {
|
System.out.println("Writing properties file failed. Reason: " + ex.getMessage());
|
} finally {
|
try {
|
if (out != null) {
|
out.close();
|
}
|
} catch (IOException e) {
|
}
|
}
|
|
validate();
|
}
|
|
private void reload() {
|
if (selectedNode.readable()) {
|
try {
|
selectedNode.reset(association);
|
} catch (ServiceError e) {
|
System.out.println("ServiceError on reading" + e.getMessage());
|
return;
|
} catch (IOException e) {
|
System.out.println("IOException on reading" + e.getMessage());
|
return;
|
}
|
validate();
|
}
|
}
|
|
private void write() {
|
if (selectedNode.writable()) {
|
try {
|
selectedNode.writeValues(association);
|
} catch (ServiceError e) {
|
System.out.println("ServiceError on writing" + e.getMessage());
|
return;
|
} catch (IOException e) {
|
System.out.println("IOException on writing" + e.getMessage());
|
return;
|
}
|
validate();
|
}
|
}
|
|
private void showDataDetails(DataTreeNode node, Counter y) {
|
if (node.getData() != null) {
|
BasicDataBind<?> data = node.getData();
|
JLabel nameLabel = data.getNameLabel();
|
nameLabel.setText(nameLabel.getText() + ": ");
|
//System.out.println("1:" + nameLabel.getText());
|
addDetailsComponent(nameLabel, 0, y.getValue(), 1, 1, 0, 0);
|
addDetailsComponent(data.getValueField(), 1, y.getValue(), 2, 1, 1, 0);
|
y.increment();
|
}
|
else {
|
for (int i = 0; i < node.getChildCount(); i++) {
|
y.increment();
|
DataObjectTreeNode childNode = (DataObjectTreeNode) node.getChildAt(i);
|
showDataDetails(childNode, childNode.toString(), y);
|
}
|
}
|
}
|
|
private void showDataDetails(DataTreeNode node, String pre, Counter y) {
|
if (node.getData() != null) {
|
BasicDataBind<?> data = node.getData();
|
JLabel nameLabel = data.getNameLabel();
|
nameLabel.setText(pre + ": ");
|
//System.out.println("2:" + nameLabel.getText() + ": " + ((JTextField)data.getValueField()).getText());
|
addDetailsComponent(nameLabel, 0, y.getValue(), 1, 1, 0, 0);
|
addDetailsComponent(data.getValueField(), 1, y.getValue(), 2, 1, 1, 0);
|
y.increment();
|
}
|
else {
|
for (int i = 0; i < node.getChildCount(); i++) {
|
y.increment();
|
DataObjectTreeNode childNode = (DataObjectTreeNode) node.getChildAt(i);
|
showDataDetails(childNode, pre + "." + childNode.toString(), y);
|
detailsPanel.add(new JSeparator());
|
addDetailsComponent(new JSeparator(), 0, y.getValue(), 3, 1, 1, 0);
|
}
|
}
|
}
|
|
private void addDetailsComponent(Component c, int x, int y, int width, int height, double weightx, double weighty) {
|
GridBagConstraints gbc = new GridBagConstraints();
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
gbc.gridx = x;
|
gbc.gridy = y;
|
gbc.gridwidth = width;
|
gbc.gridheight = height;
|
gbc.weightx = weightx;
|
gbc.weighty = weighty;
|
gbc.anchor = GridBagConstraints.NORTH;
|
gbc.insets = new Insets(3, 3, 3, 3);
|
detailsLayout.setConstraints(c, gbc);
|
detailsPanel.add(c);
|
}
|
}
|