/** * * @author greg (at) myrobotlab.org * * This file is part of MyRobotLab (http://myrobotlab.org). * * MyRobotLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version (subject to the "Classpath" exception * as provided in the LICENSE.txt file that accompanied this code). * * MyRobotLab is distributed in the hope that it will be useful or fun, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * All libraries in thirdParty bundle are subject to their own license * requirements - please refer to http://myrobotlab.org/libraries for * details. * * Enjoy ! * * */ package org.myrobotlab.control; import java.awt.Color; import java.awt.Graphics; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.image.BufferedImage; import java.util.HashMap; import java.util.List; import java.util.Random; import java.util.Vector; import javax.swing.BorderFactory; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.myrobotlab.control.widget.JIntegerField; import org.myrobotlab.framework.MRLListener; import org.myrobotlab.image.SerializableImage; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.service.GUIService; import org.myrobotlab.service.Runtime; import org.myrobotlab.service.SensorMonitor; import org.myrobotlab.service.data.Pin; import org.myrobotlab.service.data.Trigger; import org.myrobotlab.service.interfaces.SensorDataPublisher; import org.myrobotlab.service.interfaces.VideoGUISource; import org.slf4j.Logger; /** * @author Gro-G Display data sent to the SensorMonitor service. * * TODO - generalized tracing/triggering TODO - auto-sizing based on * min/max values - sizes screen * */ public class SensorMonitorGUI extends ServiceGUI implements ListSelectionListener, VideoGUISource { class AddTraceListener implements ActionListener { @Override public void actionPerformed(ActionEvent arg0) { JFrame frame = new JFrame(); frame.setTitle("add new filter"); String label = JOptionPane.showInputDialog(frame, "new trace name"); String controllerName = (String) traceController.getSelectedItem(); Color color = new Color(rand.nextInt(16777215)); traceListModel.addElement("<html><body><font color=\"" + Integer.toHexString(color.getRGB() & 0x00ffffff) + "\"> " + controllerName + " " + tracePin.getSelectedItem() + " " + label + " </font></body></html>"); // add the data to the array TraceData t = new TraceData(); t.label = label; t.color = color; t.controllerName = controllerName; t.pin = (Integer) tracePin.getSelectedItem(); traceData.put(SensorMonitor.makeKey(controllerName, t.pin), t); MRLListener MRLListener = new MRLListener("publishPin", boundServiceName, "sensorInput"); myService.send(controllerName, "addListener", MRLListener); // Notification SensorMonitor ------> GUIService // subscribe("publishSensorData", "inputSensorData", // PinData.class);// TODO-remove // already // in // attachGUI // this tells the Arduino to begin analog reads myService.send(controllerName, "analogReadPollingStart", (Integer) tracePin.getSelectedItem()); } } class AddTriggerListener implements ActionListener { @Override public void actionPerformed(ActionEvent arg0) { // TODO Auto-generated method stub JFrame frame = new JFrame(); frame.setTitle("add new filter"); // String name = JOptionPane.showInputDialog(frame, // "new alert name"); TriggerDialog triggerDlg = new TriggerDialog(); if (triggerDlg.action.equals("add")) { triggerListModel.addElement(triggerDlg.name.getText() + " " + triggerPin.getSelectedItem() + " " + triggerDlg.threshold.getInt()); // this has to be pushed to service Trigger trigger = new Trigger(); trigger.name = triggerDlg.name.getText(); trigger.pinData = new Pin(); trigger.pinData.source = triggerController.getSelectedItem().toString(); trigger.pinData.pin = (Integer) triggerPin.getSelectedItem(); trigger.threshold = triggerDlg.threshold.getInt(); myService.send(boundServiceName, "addTrigger", trigger); // add line ! with name ! & color ! } } } public class RemoveTraceListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { String name = (String) traces.getSelectedValue(); // myService.send(boundServiceName, "removeFilter", name); // TODO - block on response traceListModel.removeElement(name); String p[] = name.split(" "); String controllerName = p[2]; Integer pin = Integer.parseInt(p[3]); myService.send(controllerName, "analogReadPollingStop", pin); traceData.remove(SensorMonitor.makeKey(controllerName, pin)); } } public class RemoveTriggerListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { String name = (String) triggers.getSelectedValue(); myService.send(boundServiceName, "removeTrigger", name); // TODO - block on response triggerListModel.removeElement(name); } } class TraceData { Color color = null; String label; String controllerName; int pin; int data[] = new int[DATA_WIDTH]; int index = 0; int total = 0; int max = 0; int min = 1024; // TODO - user input on min/max int sum = 0; int mean = 0; } class TriggerDialog extends JDialog { class TriggerButtonListener implements ActionListener { TriggerDialog myDialog = null; public TriggerButtonListener(TriggerDialog d) { myDialog = d; } @Override public void actionPerformed(ActionEvent e) { action = e.getActionCommand(); myDialog.dispose(); } } private static final long serialVersionUID = 1L; public JTextField name = new JTextField(15); public JIntegerField threshold = new JIntegerField(15); public JButton add = new JButton("add"); public JButton cancel = new JButton("cancel"); public String action = null; TriggerDialog() { super(myService.getFrame(), "Trigger Dialog", true); add.setActionCommand("add"); cancel.setActionCommand("cancel"); TriggerButtonListener a = new TriggerButtonListener(this); add.addActionListener(a); cancel.addActionListener(a); JPanel panel = new JPanel(); panel.setLayout(new GridBagLayout()); gc.gridx = 0; gc.gridy = 0; panel.add(new JLabel("name : "), gc); ++gc.gridx; panel.add(name, gc); ++gc.gridy; gc.gridx = 0; panel.add(new JLabel("threshold : "), gc); ++gc.gridx; panel.add(threshold, gc); ++gc.gridy; gc.gridx = 0; ++gc.gridy; gc.gridx = 0; panel.add(add, gc); ++gc.gridx; panel.add(cancel, gc); ++gc.gridy; gc.gridx = 0; getContentPane().add(panel); pack(); setVisible(true); } } static final long serialVersionUID = 1L; public final static Logger log = LoggerFactory.getLogger(SensorMonitorGUI.class.toString()); JList traces; JList triggers; VideoWidget video = null; Thread sensorTraceThread = null; // input DefaultListModel<String> traceListModel = new DefaultListModel<String>(); DefaultListModel<String> triggerListModel = new DefaultListModel<String>(); JButton addTrace = new JButton("add"); JButton removeTrace = new JButton("remove"); JButton addTrigger = new JButton("add"); JButton removeTrigger = new JButton("remove"); JComboBox<String> traceController = null; JComboBox triggerController = null; JComboBox tracePin = null; JComboBox triggerPin = null; transient BufferedImage sensorImage = null; Graphics g = null; final int DATA_WIDTH = 320; final int DATA_HEIGHT = 512; SensorMonitor myBoundService = null; // trace data is owned by the GUIService HashMap<String, TraceData> traceData = new HashMap<String, TraceData>(); // trigger data is owned by the Service public Random rand = new Random(); public SensorMonitorGUI(final String boundServiceName, final GUIService myService, final JTabbedPane tabs) { super(boundServiceName, myService, tabs); } public Pin addTraceData(Pin pinData) { // add the data to the array TraceData t = new TraceData(); t.label = pinData.source; t.color = new Color(rand.nextInt(16777215)); t.controllerName = pinData.source; t.pin = pinData.pin; traceData.put(SensorMonitor.makeKey(pinData.source, t.pin), t); return pinData; } @Override public void attachGUI() { video.attachGUI(); subscribe("publishState", "getState", SensorMonitor.class); subscribe("addTraceData", "addTraceData", Pin.class); subscribe("publishSensorData", "inputSensorData", Pin.class); // fire the update myService.send(boundServiceName, "publishState"); } @Override public void detachGUI() { video.detachGUI(); unsubscribe("publishState", "getState", SensorMonitor.class); unsubscribe("addTraceData", "addTraceData", Pin.class); unsubscribe("publishSensorData", "inputSensorData", Pin.class); } @Override public VideoWidget getLocalDisplay() { // TODO Auto-generated method stub return video; } public void getState(SensorMonitor service) { myBoundService = service; } @Override public void init() { video = new VideoWidget(boundServiceName, myService, tabs); video.init(); addTrace.addActionListener(new AddTraceListener()); removeTrace.addActionListener(new RemoveTraceListener()); addTrigger.addActionListener(new AddTriggerListener()); removeTrigger.addActionListener(new RemoveTriggerListener()); sensorImage = new BufferedImage(DATA_WIDTH, DATA_HEIGHT, BufferedImage.TYPE_INT_RGB); g = sensorImage.getGraphics(); traces = new JList(traceListModel); traces.setFixedCellWidth(200); traces.addListSelectionListener(this); traces.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); traces.setSize(140, 160); traces.setVisibleRowCount(10); JScrollPane tracesScrollPane = new JScrollPane(traces); triggers = new JList(triggerListModel); triggers.setFixedCellWidth(200); triggers.addListSelectionListener(this); triggers.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // alerts.setSelectedIndex(clist.length-1); triggers.setSize(140, 160); triggers.setVisibleRowCount(10); JScrollPane triggersScrollPane = new JScrollPane(triggers); gc.gridx = 0; gc.gridy = 0; gc.gridheight = 2; display.add(video.display, gc); gc.gridheight = 1; // trace begin ---------------------- JPanel trace = new JPanel(new GridBagLayout()); trace.setBorder(BorderFactory.createTitledBorder("trace")); trace.add(addTrace, gc); ++gc.gridx; List<String> v = Runtime.getServiceNamesFromInterface(SensorDataPublisher.class); traceController = new JComboBox(v.toArray()); trace.add(traceController, gc); // TODO - lame, pin config is based on Arduino D. Vector<Integer> p = new Vector<Integer>(); p.addElement(0); p.addElement(1); p.addElement(2); p.addElement(3); p.addElement(4); p.addElement(5); p.addElement(6); p.addElement(7); p.addElement(8); p.addElement(9); p.addElement(10); p.addElement(11); p.addElement(12); p.addElement(13); p.addElement(14); p.addElement(15); p.addElement(16); p.addElement(17); p.addElement(18); p.addElement(19); tracePin = new JComboBox<Integer>(p); ++gc.gridx; trace.add(tracePin, gc); ++gc.gridx; trace.add(removeTrace, gc); gc.gridx = 0; ++gc.gridy; gc.gridwidth = 4; trace.add(tracesScrollPane, gc); // reusing gc for main panel gc.gridwidth = 1; gc.gridx = 1; gc.gridy = 0; display.add(trace, gc); // trace end ------------------------- // trigger begin ---------------------- JPanel triggerPanel = new JPanel(new GridBagLayout()); triggerPanel.setBorder(BorderFactory.createTitledBorder("trigger")); triggerPanel.add(addTrigger, gc); ++gc.gridx; triggerController = new JComboBox(v.toArray()); triggerPanel.add(triggerController, gc); triggerPin = new JComboBox(p); ++gc.gridx; triggerPanel.add(triggerPin, gc); ++gc.gridx; triggerPanel.add(removeTrigger, gc); gc.gridx = 0; ++gc.gridy; gc.gridwidth = 4; triggerPanel.add(triggersScrollPane, gc); // reusing gc for main panel gc.gridwidth = 1; gc.gridx = 1; gc.gridy = 1; display.add(triggerPanel, gc); // trigger end ------------------------- setCurrentFilterMouseListener(); // build filters end ------------------ } /** * method which displays the data published by the SensorMonitor on the video * widget * * @param pinData */ public void inputSensorData(Pin pinData) { // update trace array & trigger array if applicable // myService.logTime("start"); String key = SensorMonitor.makeKey(pinData); if (!traceData.containsKey(key)) { addTraceData(pinData); } TraceData t = traceData.get(key); t.index++; t.data[t.index] = pinData.value; ++t.total; t.sum += pinData.value; t.mean = t.sum / t.total; g.setColor(t.color); // g.drawRect(20, t.pin * 15 + 5, 200, 15); g.drawLine(t.index, DATA_HEIGHT - t.data[t.index - 1] / 2, t.index, DATA_HEIGHT - pinData.value / 2); // computer min max and mean // if different then blank & post to screen if (pinData.value > t.max) t.max = pinData.value; if (pinData.value < t.min) t.min = pinData.value; if (t.index < DATA_WIDTH - 1) { } else { t.index = 0; g.setColor(Color.BLACK); g.fillRect(0, 0, DATA_WIDTH, DATA_HEIGHT); g.setColor(Color.GRAY); g.drawLine(0, DATA_HEIGHT - 25, DATA_WIDTH - 1, DATA_HEIGHT - 25); g.drawString("50", 10, DATA_HEIGHT - 25); g.drawLine(0, DATA_HEIGHT - 50, DATA_WIDTH - 1, DATA_HEIGHT - 50); g.drawString("100", 10, DATA_HEIGHT - 50); g.drawLine(0, DATA_HEIGHT - 100, DATA_WIDTH - 1, DATA_HEIGHT - 100); g.drawString("200", 10, DATA_HEIGHT - 100); g.drawLine(0, DATA_HEIGHT - 200, DATA_WIDTH - 1, DATA_HEIGHT - 200); g.drawString("400", 10, DATA_HEIGHT - 200); g.drawLine(0, DATA_HEIGHT - 300, DATA_WIDTH - 1, DATA_HEIGHT - 300); g.drawString("600", 10, DATA_HEIGHT - 300); g.drawLine(0, DATA_HEIGHT - 400, DATA_WIDTH - 1, DATA_HEIGHT - 400); g.drawString("800", 10, DATA_HEIGHT - 400); g.setColor(Color.BLACK); g.fillRect(20, t.pin * 15 + 5, 200, 15); g.setColor(t.color); g.drawString(" min " + t.min + " max " + t.max + " mean " + t.mean + " total " + t.total + " sum " + t.sum, 20, t.pin * 15 + 20); } video.displayFrame(new SerializableImage(sensorImage, boundServiceName)); } public void setCurrentFilterMouseListener() { MouseListener mouseListener = new MouseAdapter() { @Override public void mouseClicked(MouseEvent mouseEvent) { JList theList = (JList) mouseEvent.getSource(); if (mouseEvent.getClickCount() == 2) { int index = theList.locationToIndex(mouseEvent.getPoint()); if (index >= 0) { Object o = theList.getModel().getElementAt(index); System.out.println("Double-clicked on: " + o.toString()); } } } }; traces.addMouseListener(mouseListener); } @Override public void valueChanged(ListSelectionEvent e) { } }