package org.yamcs.ui.eventviewer;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import org.yamcs.YamcsException;
import org.yamcs.api.YamcsApiException;
import org.yamcs.api.rest.BulkRestDataReceiver;
import org.yamcs.api.rest.RestClient;
import org.yamcs.api.ws.ConnectionListener;
import org.yamcs.api.ws.WebSocketClientCallback;
import org.yamcs.api.ws.WebSocketRequest;
import org.yamcs.api.ws.WebSocketResponseHandler;
import org.yamcs.protobuf.Web.WebSocketServerMessage.WebSocketExceptionData;
import org.yamcs.protobuf.Web.WebSocketServerMessage.WebSocketSubscriptionData;
import org.yamcs.protobuf.Yamcs.Event;
import org.yamcs.ui.YamcsConnector;
import org.yamcs.utils.TimeEncoding;
import org.yamcs.web.websocket.EventResource;
import com.google.protobuf.InvalidProtocolBufferException;
public class YamcsEventReceiver implements ConnectionListener, EventReceiver, WebSocketClientCallback, WebSocketResponseHandler {
EventViewer eventViewer;
YamcsConnector yconnector;
public YamcsEventReceiver(YamcsConnector yconnector) {
this.yconnector = yconnector;
yconnector.addConnectionListener(this);
}
@Override
public void setEventViewer(EventViewer ev) {
this.eventViewer=ev;
}
@Override
public void onMessage(WebSocketSubscriptionData data) {
if(data.hasEvent()) {
Event ev = data.getEvent();
eventViewer.addEvent(ev);
}
}
@Override
public void connected(String url) {
WebSocketRequest wsr = new WebSocketRequest(EventResource.RESOURCE_NAME, EventResource.OP_subscribe);
yconnector.performSubscription(wsr, this, this);
}
@Override
public void retrievePastEvents() {
PastEventParams params=YarchPastEventsDialog.showDialog(eventViewer);
if(!params.ok) return;
RestClient restClient = yconnector.getRestClient();
StringBuilder resource = new StringBuilder().append("/archive/"+yconnector.getConnectionParams().getInstance()+"/downloads/events?");
resource.append("start="+TimeEncoding.toString(params.start));
resource.append("&stop="+TimeEncoding.toString(params.stop));
int batchSize = 1000; //we do this to limit the number of swing calls
List<Event> evList = new ArrayList<>(batchSize);
AtomicInteger count = new AtomicInteger();
CompletableFuture<Void> f = restClient.doBulkGetRequest(resource.toString(), new BulkRestDataReceiver() {
@Override
public void receiveData(byte[] data) throws YamcsApiException {
try {
evList.add(Event.parseFrom(data));
if(evList.size()==batchSize) {
count.addAndGet(batchSize);
eventViewer.addEvents(new ArrayList<Event>(evList));
evList.clear();
}
} catch (InvalidProtocolBufferException e) {
throw new YamcsApiException("Error parsing index result: "+e.getMessage());
}
}
@Override
public void receiveException(Throwable t) {
t.printStackTrace();
eventViewer.log("Received error when downloading events: "+t.getMessage());
}
});
f.whenComplete((result, exception) -> {
if(exception==null) {
count.addAndGet(evList.size());
eventViewer.addEvents(evList);
eventViewer.log("Past Event retrieval finished; retrieved "+count.get()+" events");
} else {
exception.printStackTrace();
count.addAndGet(evList.size());
eventViewer.addEvents(evList);
eventViewer.log("Past Event retrieval finished with error; retrieved "+count.get()+" events");
}
});
}
static class PastEventParams {
boolean ok;
long start, stop;
public PastEventParams(long start, long stop) {
super();
this.start = start;
this.stop = stop;
}
}
static class YarchPastEventsDialog extends JDialog implements ActionListener {
private static final long serialVersionUID = 1L;
private PastEventParams params;
JTextField startTextField;
JTextField stopTextField;
//JCheckBox sslCheckBox;
private JTextField instanceTextField;
static YarchPastEventsDialog dialog;
YarchPastEventsDialog( JFrame parent ) {
super(parent, "Retrieve past events", true);
params = new PastEventParams(TimeEncoding.getWallclockTime()-1000L*3600*24*30, TimeEncoding.getWallclockTime());
JPanel inputPanel, buttonPanel;
JLabel lab;
JButton button;
// input panel
inputPanel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
inputPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 5, 10));
getContentPane().add(inputPanel, BorderLayout.CENTER);
lab = new JLabel("Start: ");
lab.setHorizontalAlignment(SwingConstants.RIGHT);
c.gridy=1;c.gridx=0;c.anchor=GridBagConstraints.EAST;inputPanel.add(lab,c);
startTextField = new JTextField(TimeEncoding.toString(params.start));
c.gridy=1;c.gridx=1;c.anchor=GridBagConstraints.WEST;inputPanel.add(startTextField,c);
lab = new JLabel("Stop: ");
lab.setHorizontalAlignment(SwingConstants.RIGHT);
c.gridy=2;c.gridx=0;c.anchor=GridBagConstraints.EAST;inputPanel.add(lab,c);
stopTextField = new JTextField(TimeEncoding.toString(params.stop));
c.gridy=2;c.gridx=1;c.anchor=GridBagConstraints.WEST;inputPanel.add(stopTextField,c);
// button panel
buttonPanel = new JPanel();
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
button = new JButton("OK");
button.setActionCommand("ok");
button.addActionListener(this);
getRootPane().setDefaultButton(button);
buttonPanel.add(button);
button = new JButton("Cancel");
button.setActionCommand("cancel");
button.addActionListener(this);
buttonPanel.add(button);
setMinimumSize(new Dimension(150, 100));
setLocationRelativeTo(parent);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
pack();
}
@Override
public void actionPerformed( ActionEvent e ) {
if ( e.getActionCommand().equals("ok") ) {
try {
params.start = TimeEncoding.parse(startTextField.getText());
params.stop = TimeEncoding.parse(stopTextField.getText());
params.ok = true;
setVisible(false);
} catch (NumberFormatException x) {
// do not close the dialogue
}
} else if ( e.getActionCommand().equals("cancel") ) {
params.ok = false;
setVisible(false);
}
}
public final static PastEventParams showDialog( JFrame parent) {
if(dialog==null) dialog = new YarchPastEventsDialog(parent);
dialog.setVisible(true);
return dialog.params;
}
}
@Override
public void connecting(String url) {
}
@Override
public void connectionFailed(String url, YamcsException exception) {
eventViewer.log("Connection to "+url+" failed: "+exception.getMessage());
}
@Override
public void disconnected() {
eventViewer.log("Disconnected ");
}
@Override
public void log(String message) {
}
@Override
public void onException(WebSocketExceptionData e) {
eventViewer.log("Received error on subscription: "+e.getMessage());
}
}