package org.yamcs.ui.yamcsmonitor; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.HashMap; import java.util.Comparator; import java.util.List; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.DefaultCellEditor; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.*; import org.yamcs.ui.CommandQueueControlClient; import org.yamcs.ui.CommandQueueListener; import org.yamcs.ui.YamcsConnector; import org.yamcs.protobuf.Commanding.CommandId; import org.yamcs.protobuf.Commanding.CommandQueueEntry; import org.yamcs.protobuf.Commanding.CommandQueueInfo; import org.yamcs.protobuf.Commanding.QueueState; import org.yamcs.utils.TimeEncoding; /** * Display for the telecommand queues implemented in yamcs * @author nm * */ public class CommandQueueDisplay extends JSplitPane implements ActionListener, CommandQueueListener { DefaultTableModel commandTableModel; HashMap<String,QueuesTableModel> queuesModels = new HashMap<String,QueuesTableModel>(); TableRowSorter<QueuesTableModel> queueSorter; JFrame frame; JTable queueTable, commandTable; JScrollPane queueScroll; QueuesTableModel currentQueuesModel, emptyQueuesModel; boolean isAdmin; static long oldCommandWarningTime=60; CommandQueueControlClient commandQueueControl; final String[] queueStateItems = {"BLOCKED", "DISABLED", "ENABLED"}; private volatile String selectedInstance; /** * * */ public CommandQueueDisplay(YamcsConnector yconnector, boolean isAdmin) { super(VERTICAL_SPLIT); this.isAdmin = isAdmin; commandQueueControl=new CommandQueueControlClient(yconnector); //build the table showing the queues emptyQueuesModel = new QueuesTableModel(null, null); queueTable = new JTable(emptyQueuesModel); queueTable.getTableHeader().setReorderingAllowed(false); queueTable.setAutoCreateColumnsFromModel(false); JComboBox combo = new JComboBox(queueStateItems); combo.setEditable(false); queueTable.setRowHeight(combo.getPreferredSize().height); queueTable.getColumnModel().getColumn(1).setCellEditor(new DefaultCellEditor(combo)); queueSorter = new TableRowSorter<QueuesTableModel>(emptyQueuesModel); queueSorter.setComparator(2, new Comparator<Number>() { public int compare(Number o1, Number o2) { return o1.intValue() < o2.intValue() ? -1 : (o1.intValue() > o2.intValue() ? 1 : 0); } }); queueTable.setRowSorter(queueSorter); queueTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); queueTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { //Ignore extra messages. if (e.getValueIsAdjusting()) return; int row = queueTable.getSelectedRow(); if (row != -1) { row = queueTable.convertRowIndexToModel(row); if (currentQueuesModel != null) { currentQueuesModel.setQueue(row); } } } }); queueScroll = new JScrollPane(queueTable); queueTable.setPreferredScrollableViewportSize(new Dimension(400, 150)); setTopComponent(queueScroll); //build the table showing the commands from the selected queues final String[] columnToolTips = { "The queue which contains the command", "The user who submitted the command", "Command source code", "Unique id of the command" }; final CommandRenderer cRenderer = new CommandRenderer(); final String[] commandColumns={"Queue","User","Command String","Time"}; commandTableModel = new DefaultTableModel(commandColumns,0); commandTable = new JTable(commandTableModel) { protected JTableHeader createDefaultTableHeader() { return new JTableHeader(columnModel) { public String getToolTipText(MouseEvent e) { int index = columnModel.getColumnIndexAtX(e.getPoint().x); if (index == -1) return ""; int realIndex = columnModel.getColumn(index).getModelIndex(); return columnToolTips[realIndex]; } }; } public boolean isCellEditable(int row, int column) { return false; } public TableCellRenderer getCellRenderer(int row, int column) { return column == convertColumnIndexToModel(2) ? cRenderer : super.getCellRenderer(row, column); } }; commandTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); commandTable.getTableHeader().setResizingAllowed(true); JScrollPane scroll = new JScrollPane(commandTable); commandTable.setPreferredScrollableViewportSize(new Dimension(400, 100)); setBottomComponent(scroll); //width of columns TableColumnModel model = commandTable.getTableHeader().getColumnModel(); model.getColumn(0).setPreferredWidth(60); model.getColumn(1).setPreferredWidth(80); model.getColumn(2).setPreferredWidth(500); int newWidth = new JLabel("0000-00-00T00:00:00.000").getPreferredSize().width + 20; model.getColumn(3).setPreferredWidth(newWidth); //setDividerLocation(0.5); setResizeWeight(0.1); //add right click menus final JPopupMenu cmdPopup = new JPopupMenu(); JMenuItem miSend = new JMenuItem("Send"); miSend.addActionListener(this); miSend.setActionCommand("send"); cmdPopup.add(miSend); JMenuItem miReject = new JMenuItem("Reject"); miReject.addActionListener(this); miReject.setActionCommand("reject"); cmdPopup.add(miReject); commandTable.addMouseListener(new MouseAdapter() { public void mousePressed( MouseEvent e ) { maybeShowPopup(e); } public void mouseReleased( MouseEvent e ) { maybeShowPopup(e); } private void maybeShowPopup( MouseEvent e ) { if ( e.isPopupTrigger() ) { int row = commandTable.rowAtPoint(e.getPoint()); if ((row != -1) && !commandTable.isRowSelected(row)) commandTable.setRowSelectionInterval(row, row); cmdPopup.show(e.getComponent(), e.getX(), e.getY()); } } }); commandQueueControl.addCommandQueueListener(this); } public void addProcessor(String instance, String processorName) { QueuesTableModel model = new QueuesTableModel(instance, processorName); queuesModels.put(instance+"."+processorName, model); } public void removeProcessor(String instance, String channelName) { queuesModels.remove(instance+"."+channelName); } public void setProcessor(String instance, String processorName) { currentQueuesModel = processorName == null ? emptyQueuesModel : queuesModels.get(instance+"."+processorName); if (currentQueuesModel == null) currentQueuesModel = emptyQueuesModel; queueTable.setModel(currentQueuesModel); queueSorter.setModel(currentQueuesModel); } @Override public void actionPerformed(ActionEvent ae) { final String cmd = ae.getActionCommand(); try { if (currentQueuesModel != null) { if (cmd.equals("send")) { int rows[]=commandTable.getSelectedRows(); for(int row:rows) { // PreparedCommand=currentQueuesModel.queues String queueName=(String)commandTableModel.getValueAt(commandTable.convertRowIndexToModel(row), 0); int index=commandTable.convertRowIndexToModel(row); CommandQueueEntry cqe=currentQueuesModel.getCommand(queueName, index); if(cqe==null) continue; long timeinthequeue=TimeEncoding.currentInstant()-cqe.getGenerationTime(); if(timeinthequeue>oldCommandWarningTime*1000L) { int res=CommandFateDialog.showDialog(frame, cqe.getCmdId()); switch(res) { case -1: //cancel return; case 0: //rebuild the command YamcsMonitor.theApp.log("sending command with updated time: "+cqe.getSource()); commandQueueControl.releaseCommand(cqe, true); break; case 1: //send the command with the old generation time YamcsMonitor.theApp.log("sending command: "+cqe); commandQueueControl.releaseCommand(cqe, false); break; case 2: //rejecting command System.out.println("rejecting command: "+cqe.getSource()); commandQueueControl.rejectCommand(cqe); } } else { YamcsMonitor.theApp.log("sending command: "+cqe.getSource()); commandQueueControl.releaseCommand(cqe, false); } } } else if (cmd.equals("reject")) { int rows[]=commandTable.getSelectedRows(); for (int row:rows) { String queueName=(String)commandTableModel.getValueAt(commandTable.convertRowIndexToModel(row), 0); int index=commandTable.convertRowIndexToModel(row); CommandQueueEntry cqe=currentQueuesModel.getCommand(queueName, index); if(cqe==null) continue; YamcsMonitor.theApp.log("rejecting command: "+cqe.getSource()); commandQueueControl.rejectCommand(cqe); } } } } catch (Exception e) { YamcsMonitor.theApp.showMessage(e.getMessage()); } } @Override public void log(String msg) { YamcsMonitor.theApp.log(msg); } @Override public void updateQueue(final CommandQueueInfo cqi) { if(!selectedInstance.equals(cqi.getInstance())) return; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { QueuesTableModel model = queuesModels.get(cqi.getInstance()+"."+cqi.getProcessorName()); model.updateQueue(cqi); if(cqi.getEntryCount()>0) { for(CommandQueueEntry cqe: cqi.getEntryList()) { commandAdded(cqe); } } } }); } @Override public void commandAdded(final CommandQueueEntry cqe) { if(!selectedInstance.equals(cqe.getInstance())) return; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { QueuesTableModel model = queuesModels.get(cqe.getInstance()+"."+cqe.getProcessorName()); model.commandAdded(cqe); } }); } @Override public void commandRejected(final CommandQueueEntry cqe) { if(!selectedInstance.equals(cqe.getInstance())) return; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { QueuesTableModel model=queuesModels.get(cqe.getInstance()+"."+cqe.getProcessorName()); model.removeCommandFromQueue(cqe); } }); } @Override public void commandSent(final CommandQueueEntry cqe) { if(!selectedInstance.equals(cqe.getInstance())) return; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { QueuesTableModel model=queuesModels.get(cqe.getInstance()+"."+cqe.getProcessorName()); model.removeCommandFromQueue(cqe); } }); } public void setSelectedInstance(String newInstance) { queuesModels.clear(); this.selectedInstance = newInstance; } /*contains all the queues and corresponding commands for one channel*/ class QueuesTableModel extends AbstractTableModel { List<CommandQueueInfo> queues=new ArrayList<CommandQueueInfo>(3); Map<String, ArrayList<CommandQueueEntry>> commands=new HashMap<String, ArrayList<CommandQueueEntry>>(); String instance, channel; public QueuesTableModel(String instance, String channel) { this.instance=instance; this.channel=channel; } void updateQueue(CommandQueueInfo cqi) { boolean found=false; for(int i=0;i<queues.size();i++) { CommandQueueInfo q=queues.get(i); if(q.getName().equals(cqi.getName())) { queues.set(i, cqi); found=true; fireTableRowsUpdated(i, i); break; } } if(!found) { queues.add(cqi); fireTableRowsInserted(queues.size(), queues.size()); } } void commandAdded(final CommandQueueEntry cqe) { ArrayList<CommandQueueEntry> cmds = commands.get(cqe.getQueueName()); if(cmds==null) { cmds = new ArrayList<CommandQueueEntry>(); commands.put(cqe.getQueueName(), cmds); } cmds.add(cqe); reloadCommandsTable(); } void removeCommandFromQueue(CommandQueueEntry cqe) { ArrayList<CommandQueueEntry> cmds=commands.get(cqe.getQueueName()); if(cmds==null) { return; } for(int i=0;i<cmds.size();i++) { if(cmds.get(i).getCmdId().equals(cqe.getCmdId())){ cmds.remove(i); reloadCommandsTable(); break; } } } /** * Called when a row is selected in the queue table. * Shows all the commands in the selected queues */ void setQueue(int index) { commandTableModel.setRowCount(0); CommandQueueInfo q = queues.get(index); ArrayList<CommandQueueEntry> cmds=commands.get(q.getName()); if(cmds==null) { return; } for(CommandQueueEntry cqe:cmds) { Object[] r={q.getName(), cqe.getUsername(), cqe.getSource(), TimeEncoding.toString(cqe.getGenerationTime())}; commandTableModel.addRow(r); } } void reloadCommandsTable() { if (this == currentQueuesModel) { int row = queueTable.convertRowIndexToModel(queueTable.getSelectedRow()); if (row >= 0) { setQueue(row); } } } CommandQueueEntry getCommand(String queueName, int index) { ArrayList<CommandQueueEntry> cmds=commands.get(queueName); if(cmds==null)return null; return cmds.get(index); } //private static final long serialVersionUID = 4531138066222987136L; final String[] queueColumns = {"Queue", "State", "Commands"}; public String getColumnName(int col) { return queueColumns[col]; } public int getRowCount() { return queues.size(); } public int getColumnCount() { return queueColumns.length; } public Object getValueAt(int row, int col) { CommandQueueInfo q=queues.get(row); Object o=null; switch (col) { case 0: o=q.getName(); break; case 1: o=q.getState().toString(); break; case 2: ArrayList<CommandQueueEntry> cmds=commands.get(q.getName()); if(cmds==null){ o=0; } else { o=cmds.size(); } break; } return o; } public boolean isCellEditable(int row, int col) { return col == 1; } public void setValueAt(Object value, int row, int col) { super.setValueAt(value, row, col); try { CommandQueueInfo q=queues.get(row); if (value.equals(queueStateItems[0])) { commandQueueControl.setQueueState(CommandQueueInfo.newBuilder(q).setState(QueueState.BLOCKED).build(), false); } else if (value.equals(queueStateItems[1])) { commandQueueControl.setQueueState(CommandQueueInfo.newBuilder(q).setState(QueueState.DISABLED).build(), false); } else if (value.equals(queueStateItems[2])) { boolean oldcommandsfound=false; ArrayList<CommandQueueEntry> cmds=commands.get(q.getName()); if(cmds!=null) { for(CommandQueueEntry cqe:cmds) { if(TimeEncoding.currentInstant()-cqe.getGenerationTime()>oldCommandWarningTime*1000L) { oldcommandsfound=true; break; } } } if(oldcommandsfound) { int result=CommandFateDialog.showDialog2(frame); switch(result) { case -1://cancel return; case 0: //send with updated times commandQueueControl.setQueueState(CommandQueueInfo.newBuilder(q).setState(QueueState.ENABLED).build(),true); break; case 1://send with old times commandQueueControl.setQueueState(CommandQueueInfo.newBuilder(q).setState(QueueState.ENABLED).build(),false); break; } } else { commandQueueControl.setQueueState(CommandQueueInfo.newBuilder(q).setState(QueueState.ENABLED).build(),false); } } } catch (Exception e) { e.printStackTrace(); YamcsMonitor.theApp.showMessage(e.toString()); } } } class CommandRenderer extends JTextArea implements TableCellRenderer { public void validate() {}; public void invalidate(){}; public void revalidate() {}; public void repaint(){}; public void firePropertyChange() {}; public boolean isOpaque() { return true; } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { setText(value.toString()); int height_wanted = (int)getPreferredSize().getHeight(); table.setRowHeight(row, height_wanted); if (isSelected) { setForeground(table.getSelectionForeground()); setBackground(table.getSelectionBackground()); } else { setForeground(table.getForeground()); setBackground(table.getBackground()); } return this; } } static class CommandFateDialog extends JDialog implements ActionListener { int result; static CommandFateDialog cfd1=null; static CommandFateDialog cfd2=null; JRadioButton[] radioButtons; JLabel messageLabel; public static int showDialog(Frame aFrame, CommandId cmdId) { if(cfd1==null) cfd1=new CommandFateDialog(aFrame,1); cfd1.messageLabel.setText("The command '"+cmdId.getSequenceNumber()+"' is older than "+oldCommandWarningTime+" seconds"); cfd1.setLocationRelativeTo(aFrame); cfd1.setVisible(true); return cfd1.result; } public static int showDialog2(Frame aFrame) { if(cfd2==null) cfd2=new CommandFateDialog(aFrame,2); cfd2.setLocationRelativeTo(aFrame); cfd2.setVisible(true); return cfd2.result; } public CommandFateDialog(Frame aFrame, int type) { super(aFrame, true); if(type==1) setTitle("Command older than "+oldCommandWarningTime+" seconds"); else setTitle("Command(s) older than "+oldCommandWarningTime+" seconds"); JPanel box = new JPanel(); if(type==1) messageLabel = new JLabel("The command 2009/208 17:07:17.845@255.255.255.255/123456 is older than "+oldCommandWarningTime+" seconds"); else messageLabel = new JLabel("Enabling the queue would cause some commands older than "+oldCommandWarningTime+" seconds to be sent."); box.setLayout(new BoxLayout(box, BoxLayout.PAGE_AXIS)); box.add(messageLabel); ButtonGroup group = new ButtonGroup(); if(type==1) { radioButtons = new JRadioButton[3]; radioButtons[0] = new JRadioButton("Send the command with updated generation time"); radioButtons[1] = new JRadioButton("Send the command with the current (old) generation time"); radioButtons[2] = new JRadioButton("Reject the command"); } else { radioButtons = new JRadioButton[2]; radioButtons[0] = new JRadioButton("Send all the commands with updated generation time"); radioButtons[1] = new JRadioButton("Send all the command with the current (old) generation time"); } for (int i = 0; i < radioButtons.length; i++) { box.add(radioButtons[i]); group.add(radioButtons[i]); } group.setSelected(radioButtons[0].getModel(), true); JButton cancelButton = new JButton("Cancel"); cancelButton.addActionListener(this); final JButton okButton = new JButton("OK"); okButton.setActionCommand("OK"); okButton.addActionListener(this); getRootPane().setDefaultButton(okButton); JPanel buttonPane = new JPanel(); buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS)); buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10)); buttonPane.add(Box.createHorizontalGlue()); buttonPane.add(okButton); buttonPane.add(Box.createRigidArea(new Dimension(10, 0))); buttonPane.add(cancelButton); buttonPane.add(Box.createHorizontalGlue()); Container contentPane = getContentPane(); contentPane.add(box, BorderLayout.LINE_START); contentPane.add(buttonPane, BorderLayout.PAGE_END); pack(); setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent we) { result=-1; setVisible(false); } }); } @Override public void actionPerformed(ActionEvent ev) { if(ev.getActionCommand().equalsIgnoreCase("OK")) { result=-1; for(int i=0;i<radioButtons.length;i++) { if(radioButtons[i].isSelected()) { result=i; break; } } setVisible(false); } else if(ev.getActionCommand().equalsIgnoreCase("Cancel")) { result=-1; setVisible(false); } } } public static void main(String[] args) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { JFrame frame = new JFrame("Name That Baby"); frame.pack(); // int res=CommandFateDialog.showDialog(frame," abcrada da ds"); int res=CommandFateDialog.showDialog2(frame); frame.dispose(); } }); } }