/* Copyright (c) 2011 Danish Maritime Authority.
*
* 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 dk.dma.ais.virtualnet.transponder.gui;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.border.TitledBorder;
import javax.xml.bind.JAXBException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dk.dma.ais.virtualnet.common.message.TargetTableMessage;
import dk.dma.ais.virtualnet.common.table.TargetTableEntry;
import dk.dma.ais.virtualnet.transponder.ITransponderStatusListener;
import dk.dma.ais.virtualnet.transponder.RestException;
import dk.dma.ais.virtualnet.transponder.Transponder;
import dk.dma.ais.virtualnet.transponder.TransponderConfiguration;
import dk.dma.ais.virtualnet.transponder.TransponderStatus;
/**
* Transponder frame
*/
public class TransponderFrame extends JFrame implements ActionListener, ITransponderStatusListener, WindowListener {
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(TransponderFrame.class);
private final String conffile;
private final boolean embedded;
private Transponder transponder;
private TransponderConfiguration conf;
// Control buttons
private final JButton startButton = new JButton("Start");
private final JButton stopButton = new JButton("Stop");
private final JButton closeExitButton = new JButton("Exit/Close");
private final JButton selectVesselButton = new JButton("...");
// Input fields
private final JTextField mmsi = new JTextField(9);
private final JTextField resendInterval = new JTextField(2);
private final JTextField serverHost = new JTextField(20);
private final JTextField serverPort = new JTextField(5);
private final JTextField username = new JTextField();
private final JPasswordField password = new JPasswordField();
private final JTextField port = new JTextField();
private final JTextField receiveRadius = new JTextField();
// Status labels
private final JLabel clientStatusIconLabel = new JLabel();
private final JLabel serverStatusIconLabel = new JLabel();
private final JLabel serverErrorLabel = new JLabel();
private final JLabel ownShipPosIconLabel = new JLabel();
private final JLabel shipNameLbl = new JLabel();
// Icons
private static final ImageIcon UNKNOWN_ICON = new ImageIcon(TransponderFrame.class.getResource("/images/UNKNOWN.png"));
private static final ImageIcon ERROR_ICON = new ImageIcon(TransponderFrame.class.getResource("/images/ERROR.png"));
private static final ImageIcon OK_ICON = new ImageIcon(TransponderFrame.class.getResource("/images/OK.png"));
private final List<JComponent> lockedWhileRunningComponents = Arrays.asList(new JComponent[] { mmsi, resendInterval,
serverHost, serverPort, username, password, port, receiveRadius });
private final List<JLabel> iconLabels = Arrays.asList(new JLabel[] { clientStatusIconLabel, serverStatusIconLabel,
ownShipPosIconLabel });
public TransponderFrame() {
this("transponder.xml");
}
public TransponderFrame(String conffile) {
this(conffile, false, null);
}
public TransponderFrame(boolean embeeded) {
this("transponder.xml", embeeded, null);
}
public TransponderFrame(String conffile, boolean embedded, JFrame parent) {
super();
this.conffile = conffile;
this.embedded = embedded;
setSize(new Dimension(420, 500));
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
addWindowListener(this);
setResizable(false);
setTitle("AisVirtualNet transponder");
setLocationRelativeTo(parent);
startButton.addActionListener(this);
stopButton.addActionListener(this);
selectVesselButton.addActionListener(this);
closeExitButton.addActionListener(this);
closeExitButton.setText(embedded ? "Close" : "Exit");
layoutGui();
loadConf();
saveConf();
// Update GUI components with configuration values
updateValues();
// Update controls
updateEnabled();
}
/**
* Update status components with transponder state
*
* @param status
*/
void updateStatus(TransponderStatus status) {
if (transponder == null) {
return;
}
// Determine client status
clientStatusIconLabel.setIcon(status.isClientConnected() ? OK_ICON : ERROR_ICON);
// Determine server status
serverStatusIconLabel.setIcon(status.isServerConnected() ? OK_ICON : ERROR_ICON);
// Set possible server error
serverErrorLabel.setText(status.getServerError() != null ? status.getServerError() : "");
// Own pos indicating
ownShipPosIconLabel.setIcon(status.getOwnPos() != null ? OK_ICON : ERROR_ICON);
// Own ship name
shipNameLbl.setText(status.getShipName());
}
/**
* Update configuration from GUI components
*/
private void updateConf() {
conf.setOwnMmsi(getInt(mmsi));
conf.setOwnPosInterval(getInt(resendInterval));
conf.setServerHost(getString(serverHost));
conf.setServerPort(getInt(serverPort));
conf.setUsername(getString(username));
conf.setPassword(getString(password));
conf.setPort(getInt(port));
conf.setReceiveRadius(getInt(receiveRadius) * 1852);
}
private String getString(JTextField field) {
return field.getText();
}
private int getInt(JTextField field) {
try {
return Integer.parseInt(field.getText());
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid integer: " + field.getText());
}
}
/**
* Update values in components from conf
*/
private void updateValues() {
setVal(mmsi, conf.getOwnMmsi());
setVal(resendInterval, conf.getOwnPosInterval());
setVal(serverHost, conf.getServerHost());
setVal(serverPort, conf.getServerPort());
setVal(username, conf.getUsername());
setVal(password, conf.getPassword());
setVal(port, conf.getPort());
setVal(receiveRadius, conf.getReceiveRadius() / 1852);
}
private void setVal(JTextField field, String val) {
field.setText(val);
}
private void setVal(JTextField field, int val) {
setVal(field, Integer.toString(val));
}
private void updateEnabled() {
startButton.setEnabled(transponder == null);
stopButton.setEnabled(transponder != null);
selectVesselButton.setEnabled(transponder == null);
for (JComponent comp : lockedWhileRunningComponents) {
comp.setEnabled(transponder == null);
}
if (transponder == null) {
for (JLabel iconLabel : iconLabels) {
iconLabel.setIcon(UNKNOWN_ICON);
}
serverErrorLabel.setText("");
shipNameLbl.setText("");
}
}
@Override
public void stateChanged(final TransponderStatus status) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
updateStatus(status);
}
});
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == startButton) {
startTransponder();
} else if (e.getSource() == stopButton) {
stopTransponder();
} else if (e.getSource() == selectVesselButton) {
selectTarget();
} else if (e.getSource() == closeExitButton) {
close();
}
}
private void selectTarget() {
try {
updateConf();
} catch (IllegalArgumentException e) {
JOptionPane.showMessageDialog(this, e.getMessage(), "Transponder error", JOptionPane.ERROR_MESSAGE);
return;
}
Cursor currentCursor = getCursor();
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
TargetTableMessage message;
try {
message = Transponder.getTargets(conf.getServerHost(), conf.getServerPort(), conf.getUsername(), conf.getPassword());
} catch (RestException e) {
LOG.error("Failed to get list of targets");
JOptionPane.showMessageDialog(this, "Failed to get list of targets: " + e.getMessage(), "Error",
JOptionPane.ERROR_MESSAGE);
setCursor(currentCursor);
return;
}
setCursor(currentCursor);
// Sort by name
List<TargetTableEntry> targets = message.getTargets();
Collections.sort(targets, new TargetTableEntry.NameSort());
// Show select dialog
SelectTargetDialog selectTargetDialog = new SelectTargetDialog(this, targets);
selectTargetDialog.setVisible(true);
if (selectTargetDialog.getSelectedTarget() != null) {
conf.setOwnMmsi(selectTargetDialog.getSelectedTarget().intValue());
updateValues();
}
}
public void startTransponder() {
if (transponder != null) {
LOG.error("Trying to start transponder already started");
return;
}
try {
updateConf();
} catch (IllegalArgumentException e) {
JOptionPane.showMessageDialog(this, e.getMessage(), "Transponder error", JOptionPane.ERROR_MESSAGE);
return;
}
try {
transponder = new Transponder(conf);
} catch (IOException e) {
transponder = null;
JOptionPane.showMessageDialog(this, e.getMessage(), "Transponder error", JOptionPane.ERROR_MESSAGE);
return;
}
saveConf();
updateEnabled();
// Make the GUI the listener of events
transponder.getStatus().addListener(this);
transponder.start();
}
private void stopTransponder() {
if (transponder == null) {
LOG.error("Trying to stop transponder already stopped");
return;
}
transponder.shutdown();
transponder = null;
updateEnabled();
}
private void loadConf() {
try {
conf = TransponderConfiguration.load(conffile);
} catch (FileNotFoundException e) {
} catch (JAXBException e) {
LOG.error("Failed to load configuration", e);
}
if (conf == null) {
conf = new TransponderConfiguration();
}
}
private void saveConf() {
try {
TransponderConfiguration.save(conffile, conf);
} catch (Exception e) {
LOG.error("Failed to save configuration", e);
}
}
private void layoutGui() {
getContentPane().setLayout(null);
JPanel mmsiPanel = new JPanel();
mmsiPanel.setBounds(6, 6, 400, 66);
mmsiPanel.setLayout(null);
mmsiPanel.setBorder(new TitledBorder(null, "MMSI", TitledBorder.LEADING, TitledBorder.TOP, null, null));
mmsi.setBounds(16, 22, 105, 28);
mmsiPanel.add(mmsi);
selectVesselButton.setBounds(121, 23, 47, 29);
mmsiPanel.add(selectVesselButton);
resendInterval.setBounds(344, 22, 38, 28);
mmsiPanel.add(resendInterval);
JLabel omLbl = new JLabel("Own message interval (s)");
omLbl.setBounds(180, 28, 157, 16);
mmsiPanel.add(omLbl);
getContentPane().add(mmsiPanel);
JPanel serverPanel = new JPanel();
serverPanel.setLayout(null);
serverPanel.setBounds(6, 84, 400, 88);
serverPanel.setBorder(new TitledBorder(null, "Server", TitledBorder.LEADING, TitledBorder.TOP, null, null));
JLabel srvHostLbl = new JLabel("Host");
srvHostLbl.setBounds(16, 23, 37, 16);
serverPanel.add(srvHostLbl);
JLabel srvPortLbl = new JLabel("Port");
srvPortLbl.setBounds(291, 20, 37, 16);
serverPanel.add(srvPortLbl);
serverPort.setBounds(324, 17, 58, 22);
serverPanel.add(serverPort);
JLabel usernameLbl = new JLabel("Username");
serverPanel.add(usernameLbl);
usernameLbl.setBounds(16, 53, 69, 16);
username.setBounds(89, 50, 115, 22);
serverPanel.add(username);
JLabel passwordLbl = new JLabel("Password");
passwordLbl.setBounds(216, 50, 69, 16);
serverPanel.add(passwordLbl);
password.setBounds(280, 47, 102, 22);
serverPanel.add(password);
getContentPane().add(serverPanel);
serverHost.setBounds(89, 20, 190, 22);
serverPanel.add(serverHost);
JPanel transponderPanel = new JPanel();
transponderPanel.setBorder(new TitledBorder(null, "Transponder", TitledBorder.LEADING, TitledBorder.TOP, null, null));
transponderPanel.setLayout(null);
transponderPanel.setBounds(6, 184, 400, 60);
JLabel portLbl = new JLabel("Port");
portLbl.setBounds(16, 23, 34, 16);
transponderPanel.add(portLbl);
JLabel radiusLbl = new JLabel("Receive radius (nm) (0 = all)");
radiusLbl.setBounds(119, 23, 193, 16);
transponderPanel.add(radiusLbl);
port.setBounds(49, 20, 58, 22);
transponderPanel.add(port);
receiveRadius.setBounds(324, 20, 58, 22);
transponderPanel.add(receiveRadius);
getContentPane().add(transponderPanel);
JPanel statusPanel = new JPanel();
statusPanel.setLayout(null);
statusPanel.setBorder(new TitledBorder(null, "Status", TitledBorder.LEADING, TitledBorder.TOP, null, null));
statusPanel.setBounds(6, 256, 400, 123);
JLabel clientLbl = new JLabel("Client connection");
clientLbl.setBounds(16, 44, 117, 16);
statusPanel.add(clientLbl);
statusPanel.add(clientStatusIconLabel);
clientStatusIconLabel.setBounds(145, 44, 16, 16);
JLabel serverLbl = new JLabel("Server connection");
serverLbl.setBounds(16, 23, 117, 16);
statusPanel.add(serverLbl);
serverStatusIconLabel.setBounds(145, 23, 16, 16);
statusPanel.add(serverStatusIconLabel);
serverErrorLabel.setForeground(Color.RED);
serverErrorLabel.setText("");
serverErrorLabel.setFont(new Font("Lucida Grande", Font.PLAIN, 10));
serverErrorLabel.setBounds(182, 25, 200, 15);
statusPanel.add(serverErrorLabel);
JLabel ownPosLbl = new JLabel("Own position");
ownPosLbl.setBounds(16, 66, 117, 16);
statusPanel.add(ownPosLbl);
ownShipPosIconLabel.setBounds(145, 66, 16, 16);
statusPanel.add(ownShipPosIconLabel);
JLabel nameLbl = new JLabel("Ship name");
nameLbl.setBounds(16, 89, 117, 16);
statusPanel.add(nameLbl);
shipNameLbl.setBounds(145, 89, 237, 16);
statusPanel.add(shipNameLbl);
getContentPane().add(statusPanel);
JPanel controlPanel = new JPanel();
controlPanel.setBorder(new TitledBorder(null, "Controls", TitledBorder.LEADING, TitledBorder.TOP, null, null));
controlPanel.setBounds(6, 391, 400, 75);
controlPanel.setLayout(null);
startButton.setBounds(6, 25, 75, 29);
controlPanel.add(startButton);
stopButton.setBounds(87, 25, 75, 29);
controlPanel.add(stopButton);
getContentPane().add(controlPanel);
closeExitButton.setBounds(319, 25, 75, 29);
controlPanel.add(closeExitButton);
}
public void close() {
saveConf();
if (embedded) {
this.setVisible(false);
} else {
shutdown();
System.exit(0);
}
}
public void shutdown() {
if (transponder != null) {
stopTransponder();
}
this.dispose();
}
@Override
public void windowClosing(WindowEvent e) {
close();
}
@Override
public void windowOpened(WindowEvent e) {
}
@Override
public void windowClosed(WindowEvent e) {
}
@Override
public void windowIconified(WindowEvent e) {
}
@Override
public void windowDeiconified(WindowEvent e) {
}
@Override
public void windowActivated(WindowEvent e) {
}
@Override
public void windowDeactivated(WindowEvent e) {
}
}