package org.yamcs.ui.archivebrowser;
import org.yamcs.protobuf.Yamcs.ReplayRequest;
import org.yamcs.protobuf.Yamcs.ReplaySpeed;
import org.yamcs.protobuf.Yamcs.ReplayStatus.ReplayState;
import org.yamcs.protobuf.YamcsManagement.ProcessorInfo;
import org.yamcs.protobuf.YamcsManagement.Statistics;
import org.yamcs.protobuf.YamcsManagement.TmStatistics;
import org.yamcs.ui.ProcessorControlClient;
import org.yamcs.utils.TimeEncoding;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Panel containing the replay controls (start/stop, start/stop/current time)
* @author nm
*
*/
public class ReplayPanel extends JPanel {
protected JLabel replayStartLabel, replayCurrentLabel, replayStopLabel, channelNameLabel, replayStatusLabel, replaySpeedLabel;
protected ImageIcon replayStartIcon, replayStopIcon;
protected JButton playStopButton;
public JButton applySelectionButton;
protected ProcessorInfo currentYProcInfo;
int replayButtonFunction;
static final int STOP = 0;
static final int PLAY = 1;
DataViewer dataViewer;
ProcessorControlClient channelControl;
long currentInstant;
public ReplayPanel() {
super(new BorderLayout());
GridBagLayout lay = new GridBagLayout();
JPanel centerPanel = new JPanel(lay);
// playing/stopped status
GridBagConstraints gbc=new GridBagConstraints();
replayStatusLabel = new JLabel();
gbc.weightx = 1.0; gbc.gridwidth = 1;
lay.setConstraints(replayStatusLabel, gbc);
centerPanel.add(replayStatusLabel);
// replay: channel name
JLabel lab = new JLabel("Name:");
gbc.weightx = 0.0; gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.fill = GridBagConstraints.BOTH; gbc.weighty = 1.0; gbc.anchor = GridBagConstraints.EAST;
lay.setConstraints(lab, gbc);
centerPanel.add(lab);
channelNameLabel = new JLabel();
channelNameLabel.setPreferredSize(new Dimension(150, channelNameLabel.getPreferredSize().height));
gbc.weightx = 1.0; gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.anchor = GridBagConstraints.WEST;
lay.setConstraints(channelNameLabel, gbc);
centerPanel.add(channelNameLabel);
// play/stop button
replayStartIcon = ArchivePanel.getIcon("start.gif");
replayStopIcon = ArchivePanel.getIcon("stop.gif");
playStopButton = new JButton(replayStopIcon); // the Play/Stop button
playStopButton.setEnabled(false);
playStopButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed( ActionEvent ae ) {
playOrStopPressed();
}
});
gbc.weightx = 0.0; gbc.weighty = 1.0; gbc.gridwidth = 1; gbc.gridheight = 4; gbc.anchor = GridBagConstraints.NORTH;
gbc.fill = GridBagConstraints.NONE;
lay.setConstraints(playStopButton, gbc);
centerPanel.add(playStopButton);
// replay: start time
lab = new JLabel("Start:");
gbc.weightx = 0.0; gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.fill = GridBagConstraints.BOTH; gbc.weighty = 1.0; gbc.anchor = GridBagConstraints.EAST;
lay.setConstraints(lab, gbc);
centerPanel.add(lab);
replayStartLabel = new JLabel();
replayStartLabel.setPreferredSize(new Dimension(150, replayStartLabel.getPreferredSize().height));
gbc.weightx = 1.0; gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.anchor = GridBagConstraints.WEST;
lay.setConstraints(replayStartLabel, gbc);
centerPanel.add(replayStartLabel);
// replay: current time
lab = new JLabel("Current:");
gbc.weightx = 0.0; gbc.gridwidth = 1;
gbc.anchor = GridBagConstraints.EAST;
lay.setConstraints(lab, gbc);
centerPanel.add(lab);
replayCurrentLabel = new JLabel();
replayCurrentLabel.setPreferredSize(new Dimension(150, replayCurrentLabel.getPreferredSize().height));
gbc.weightx = 1.0; gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.anchor = GridBagConstraints.WEST;
lay.setConstraints(replayCurrentLabel, gbc);
centerPanel.add(replayCurrentLabel);
// replay: stop time
lab = new JLabel("Stop:");
gbc.weightx = 0.0; gbc.gridwidth = 1;
gbc.anchor = GridBagConstraints.EAST;
lay.setConstraints(lab, gbc);
centerPanel.add(lab);
replayStopLabel = new JLabel();
replayStopLabel.setPreferredSize(new Dimension(150, replayStopLabel.getPreferredSize().height));
gbc.weightx = 1.0; gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.anchor = GridBagConstraints.WEST;
lay.setConstraints(replayStopLabel, gbc);
centerPanel.add(replayStopLabel);
// replay: speed
lab = new JLabel("Speed:");
gbc.weightx = 0.0; gbc.gridwidth = 1;
gbc.anchor = GridBagConstraints.EAST;
lay.setConstraints(lab, gbc);
centerPanel.add(lab);
replaySpeedLabel = new JLabel();
replaySpeedLabel.setPreferredSize(new Dimension(150, replaySpeedLabel.getPreferredSize().height));
gbc.weightx = 1.0; gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.anchor = GridBagConstraints.WEST;
lay.setConstraints(replaySpeedLabel, gbc);
centerPanel.add(replaySpeedLabel);
add(centerPanel, BorderLayout.CENTER);
Box buttonPanel = Box.createVerticalBox();
buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 50, 0, 50));
applySelectionButton = new JButton("Apply Selection");
applySelectionButton.setAlignmentX(Component.CENTER_ALIGNMENT);
applySelectionButton.setEnabled(false);
applySelectionButton.setToolTipText("Apply the selection to the replay");
applySelectionButton.setActionCommand("apply");
buttonPanel.add(Box.createVerticalGlue());
buttonPanel.add(applySelectionButton);
buttonPanel.add(Box.createVerticalGlue());
add(buttonPanel, BorderLayout.EAST);
}
public void setDataViewer(DataViewer dataViewer) {
this.dataViewer=dataViewer;
}
public void setProcessorControlClient(ProcessorControlClient cc) {
this.channelControl=cc;
}
void playOrStopPressed() {
if ( currentYProcInfo != null ) {
if ( replayButtonFunction == STOP ) {
pauseReplay();
} else {
resumeReplay();
}
}
}
public void clearReplayPanel() {
currentYProcInfo = null;
for ( Component c:getComponents() ) {
c.setEnabled(false);
}
replayStartLabel.setText("");
replayStopLabel.setText("");
replayCurrentLabel.setText("");
//replayStartLabel.setPreferredSize(new Dimension(150, replayStartLabel.getPreferredSize().height));
dataViewer.getDataView().setStartLocator(dataViewer.getDataView().DO_NOT_DRAW);
dataViewer.getDataView().setStopLocator(dataViewer.getDataView().DO_NOT_DRAW);
dataViewer.getDataView().setCurrentLocator(dataViewer.getDataView().DO_NOT_DRAW);
}
/**
* called by the yamcs monitor when a channelinfo update is received from the server
* @param ci
*/
public void updateProcessorInfol(ProcessorInfo ci) {
if((currentYProcInfo==null)
|| !ci.getInstance().equals(currentYProcInfo.getInstance())
|| !ci.getName().equals(currentYProcInfo.getName())
|| !ci.hasReplayRequest()) return ;
currentYProcInfo = ci;
updateReplayPanel();
}
/**
* called by yamcs monitor when the selected channel has changed
* @param ci
*/
public void setupReplayPanel(ProcessorInfo ci) {
if(ci.hasReplayRequest()) {
currentYProcInfo = ci;
if ( isVisible() ) {
playStopButton.setEnabled(true);
replayCurrentLabel.setText("");
dataViewer.getDataView().setCurrentLocator(dataViewer.getDataView().DO_NOT_DRAW);
for ( Component c:getComponents() ) {
c.setEnabled(true);
}
updateReplayPanel();
}
} else {
clearReplayPanel();
}
}
private void updateReplayPanel() {
if ( currentYProcInfo.getReplayState()==ReplayState.RUNNING ) {
if ( replayStopIcon == null ) {
playStopButton.setText("Stop");
} else {
playStopButton.setIcon(replayStopIcon);
}
playStopButton.setToolTipText("Stop replay and remain at the current position.");
replayButtonFunction = STOP;
} else {
if ( replayStartIcon == null ) {
playStopButton.setText("Play");
} else {
playStopButton.setIcon(replayStartIcon);
}
playStopButton.setToolTipText("Start replay from the current position.");
replayButtonFunction = PLAY;
}
ReplayRequest rr=currentYProcInfo.getReplayRequest();
replayStartLabel.setText(TimeEncoding.toString(rr.getStart()));
replayStopLabel.setText(TimeEncoding.toString(rr.getStop()));
replaySpeedLabel.setText(getSpeedLabel(rr.getSpeed()));
channelNameLabel.setText(currentYProcInfo.getName());
// draw start/stop locators
dataViewer.getDataView().setStartLocator(rr.getStart());
dataViewer.getDataView().setStopLocator(rr.getStop());
}
private String getSpeedLabel(ReplaySpeed speed) {
switch(speed.getType()) {
case AFAP:
return "As fast as possible";
case FIXED_DELAY:
return "Fixed delay "+(int)speed.getParam()+" ms";
case REALTIME:
return "Realtime x"+speed.getParam();
}
return "unknown";
}
public void updateStatistics(Statistics stats) {
// invoked frequently by YamcsMonitor.updateTmStatsTable()
if ((currentYProcInfo != null) &&
currentYProcInfo.hasReplayRequest() &&
currentYProcInfo.getInstance().equals(stats.getInstance()) &&
currentYProcInfo.getName().equals(stats.getYProcessorName())) {
// find the timestamp of the most recent packet received
long pos = 0;
for( TmStatistics ts:stats.getTmstatsList()) {
pos = Math.max(pos, ts.getLastPacketTime());
}
currentInstant=pos;
replayCurrentLabel.setText(TimeEncoding.toString(currentInstant));
dataViewer.getDataView().setCurrentLocator(currentInstant);
}
}
void pauseReplay() {
try {
channelControl.pauseArchiveReplay(currentYProcInfo.getInstance(), currentYProcInfo.getName());
} catch (Exception e) {
debugLog("exception when stopping replay for channel "+currentYProcInfo.getName()+" :"+e.getMessage());
}
}
void resumeReplay() {
if ( currentInstant == TimeEncoding.INVALID_INSTANT ) {
debugLog("start replay from " + TimeEncoding.toString(currentYProcInfo.getReplayRequest().getStart()));
} else {
debugLog("start replay from " + TimeEncoding.toString(currentInstant));
}
try {
channelControl.resumeArchiveReplay(currentYProcInfo.getInstance(), currentYProcInfo.getName());
} catch (Exception e) {
debugLog("exception when starting replay for channel "+currentYProcInfo.getInstance()+"."+currentYProcInfo.getName()+" :"+e.getMessage());
}
}
void seekReplay(long newPosition) {
debugLog("seeking replay to " + TimeEncoding.toString(newPosition));
try {
channelControl.seekArchiveReplay(currentYProcInfo.getInstance(), currentYProcInfo.getName(), newPosition);
} catch (Exception e) {
debugLog("exception when starting replay for channel "+currentYProcInfo.getInstance()+"."+currentYProcInfo.getName()+" :"+e.getMessage());
}
}
private void debugLog(String string) {
System.err.println(string);
}
public void channelStateChanged(ProcessorInfo ci) {
currentYProcInfo=ci;
updateReplayPanel();
}
}