package org.yamcs.ui;
import java.awt.Component;
import java.awt.Container;
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.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.ProgressMonitor;
import javax.swing.SwingUtilities;
import org.yamcs.api.YamcsConnectionProperties;
import org.yamcs.api.rest.RestClient;
import org.yamcs.protobuf.Yamcs.TmPacketData;
import org.yamcs.utils.CcsdsPacket;
import org.yamcs.utils.PacketFormatter;
import org.yamcs.utils.TimeEncoding;
/**
* @author nm
* GUI for requesting packet dumps
*/
public class PacketRetrievalGui extends JFrame implements ActionListener {
JCheckBox withoutCcsds;
JCheckBox pactsFakeHeaders;
JCheckBox printHex;
JButton startStop;
JFileChooser fileChooser;
List<String> packetNames;
long startInstant, stopInstant;
String archiveInstance;
Component parent;
ProgressMonitor progressMonitor;
private File outputFile;
private OutputStream outputStream;
YamcsConnectionProperties connectionParams;
PacketFormatter packetFormatter;
private CompletableFuture<Void> completableFuture;
/**
* Creates a new window that requests parameter deliveries
*
*/
public PacketRetrievalGui(YamcsConnectionProperties connectionParams, Component parent) {
super("Dump Telemetry Packets");
this.connectionParams = connectionParams;
this.parent = parent;
Container frameContentPane=getContentPane();
frameContentPane.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
JLabel label=new JLabel("Dumping the selected telemetry packets into a file.");
gbc.gridx=0;gbc.gridy=0;gbc.gridwidth=GridBagConstraints.REMAINDER;
//gbc.ipadx=5; gbc.ipady=5;
gbc.insets=new Insets(5,5,5,5);
frameContentPane.add(label,gbc);
//options
JPanel optionsPanel=new JPanel();
optionsPanel.setLayout(new BoxLayout(optionsPanel,BoxLayout.PAGE_AXIS));
optionsPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Options"),BorderFactory.createEmptyBorder(5,5,5,5)));
withoutCcsds=new JCheckBox("Remove the CCSDS headers");
optionsPanel.add(withoutCcsds);
printHex=new JCheckBox("Print in hexadecimal rather than binary");
optionsPanel.add(printHex);
pactsFakeHeaders=new JCheckBox("Add a fake 32 bytes PaCTS header");
optionsPanel.add(pactsFakeHeaders);
gbc.gridx=0;gbc.gridy=1;gbc.gridwidth=GridBagConstraints.REMAINDER;gbc.weightx = 1.0;gbc.fill = GridBagConstraints.HORIZONTAL;
frameContentPane.add(optionsPanel,gbc);
gbc.insets=new Insets(2,2,2,2);
fileChooser=new JFileChooser("Select Output Directory");
fileChooser.setApproveButtonText("Save");
fileChooser.addActionListener(this);
gbc.gridy=2;gbc.gridx=0;gbc.gridwidth=GridBagConstraints.REMAINDER;gbc.fill = GridBagConstraints.BOTH;
gbc.weighty = 1.0;gbc.weightx = 1.0;
frameContentPane.add(fileChooser,gbc);
pack();
}
public void setValues(String archiveInstance, List<String> packetNames, long start, long stop) {
this.packetNames=packetNames;
this.startInstant=start;
this.stopInstant=stop;
this.archiveInstance=archiveInstance;
if (packetNames.size() > 0) {
final String prefix = packetNames.get(0).split("_", 2)[0]; // use the prefix of the first packet name
String startWinCompatibleDateTime = TimeEncoding.toWinCompatibleDateTime(startInstant);
String stopWinCompatibleDateTime = TimeEncoding.toWinCompatibleDateTime(stopInstant);
String fileName = String.format("%s_packets_%s_%s.dump"
,prefix
,startWinCompatibleDateTime
,stopWinCompatibleDateTime);
fileChooser.setSelectedFile(new File(fileChooser.getSelectedFile(), fileName));
}
}
@Override
public void actionPerformed(ActionEvent ae) {
String cmd = ae.getActionCommand();
if(cmd.equals("CancelSelection")) {
setVisible(false);
} else {
outputFile=fileChooser.getSelectedFile();
if(outputFile.exists()) {
if(JOptionPane.showConfirmDialog(this, "Are you sure you want to overwrite "+outputFile,"Overwrite file?",JOptionPane.YES_NO_OPTION,JOptionPane.WARNING_MESSAGE)
==JOptionPane.NO_OPTION) {
return;
}
}
setVisible(false);
count = 0;
downloadSize=0;
downloadStartTime=System.currentTimeMillis();
progressMonitor=new ProgressMonitor(parent,"Saving packets","0 packets saved",0,(int)((stopInstant-startInstant)/1000));
try {
outputStream = new BufferedOutputStream(new FileOutputStream(outputFile));
packetFormatter=new PacketFormatter(outputStream);
packetFormatter.setHex(printHex.isSelected());
packetFormatter.setWithoutCcsds(withoutCcsds.isSelected());
packetFormatter.setWithPacts(pactsFakeHeaders.isSelected());
YamcsConnectionProperties ycd = (YamcsConnectionProperties)connectionParams;
ycd.setInstance(archiveInstance);
RestClient restClient = new RestClient(ycd);
StringBuilder sb = new StringBuilder();
sb.append("/archive/").append(archiveInstance).append("/downloads/packets")
.append("?start=").append(TimeEncoding.toString(startInstant))
.append("&stop=").append(TimeEncoding.toString(stopInstant))
.append("&name=");
boolean first = true;
for(String pn:packetNames) {
if(first) {
first = false;
} else {
sb.append(",");
}
sb.append(pn);
}
completableFuture = restClient.doBulkGetRequest(sb.toString(), (data) -> {
TmPacketData tmpacket;
try {
tmpacket = TmPacketData.parseFrom(data);
packetReceived(new CcsdsPacket(tmpacket.getPacket().asReadOnlyByteBuffer()));
} catch (Exception e) {
e.printStackTrace();
}
});
completableFuture.whenComplete((v, t) ->{
if((t==null) || (t instanceof CancellationException)) {
replayFinished();
} else {
exception(t);
}
});
} catch (FileNotFoundException e1) {
JOptionPane.showMessageDialog(parent, "Cannot open file: "+e1.getMessage(),"Cannot open file",JOptionPane.ERROR_MESSAGE);
} catch (Exception e) {
JOptionPane.showMessageDialog(parent, "Exception when retrieving data: "+e.getMessage(),"Exception when retrieving data",JOptionPane.ERROR_MESSAGE);
}
}
}
int count;
long downloadSize;
long downloadStartTime;
public void packetReceived(CcsdsPacket c) {
int progr=(int)((c.getInstant()-startInstant)/1000);
count++;
downloadSize+=c.getLength();
if(count%100==0) progressMonitor.setNote(count+" packets received");
progressMonitor.setProgress(progr);
if(progressMonitor.isCanceled()) completableFuture.cancel(true);
try {
packetFormatter.writePacket(c);
} catch (IOException e) {
e.printStackTrace();
}
}
private void replayFinished() {
SwingUtilities.invokeLater(
new Runnable() {
@Override
public void run() {
try {
packetFormatter.close();
} catch (IOException e) {
JOptionPane.showMessageDialog(parent, "Error when closing the output file: "+e.getMessage(), "Error when closing the output file", JOptionPane.ERROR_MESSAGE);
}
if(progressMonitor.isCanceled()) {
JOptionPane.showMessageDialog(parent, "Retrieval canceled. "+count+" packets retrieved");
} else {
progressMonitor.close();
float speed=(downloadSize*1000)/(1024*(System.currentTimeMillis()-downloadStartTime));
JOptionPane.showMessageDialog(parent, "The packet retrieval finished successfully. "+count+" packets retrieved in "+outputFile+". Retrieval speed: "+speed+" KiB/sec");
}
}
});
}
public void exception(final Throwable e) {
final String message;
message=e.toString();
SwingUtilities.invokeLater(
new Runnable() {
@Override
public void run() {
JOptionPane.showMessageDialog(parent, message, message, JOptionPane.ERROR_MESSAGE);
progressMonitor.close();
}
});
}
}