/**
*
* @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.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.myrobotlab.control.widget.ImageButton;
import org.myrobotlab.reflection.Reflector;
import org.myrobotlab.service.Arduino;
import org.myrobotlab.service.GUIService;
import org.myrobotlab.service.Motor;
import org.myrobotlab.service.Runtime;
import org.myrobotlab.service.data.Pin;
import org.myrobotlab.service.interfaces.MotorController;
import org.myrobotlab.service.interfaces.ServiceInterface;
public class MotorGUI extends ServiceGUI implements ActionListener, ChangeListener {
public class FloatJSlider extends JSlider {
private static final long serialVersionUID = 1L;
final int scale;
public FloatJSlider(int min, int max, int value, int scale) {
super(min, max, value);
this.scale = scale;
Hashtable<Integer, JLabel> labelTable = new Hashtable<Integer, JLabel>();
labelTable.put(new Integer(min), new JLabel(String.format("%.2f", (float) min / scale)));
labelTable.put(new Integer(min / 2), new JLabel(String.format("%.2f", (float) min / scale / 2)));
labelTable.put(new Integer(value), new JLabel(String.format("%.2f", (float) value / scale)));
labelTable.put(new Integer(max / 2), new JLabel(String.format("%.2f", (float) max / scale / 2)));
labelTable.put(new Integer(max), new JLabel(String.format("%.2f", (float) max / scale)));
setLabelTable(labelTable);
setPaintTrack(false);
}
public float getScaledValue() {
return ((float) super.getValue()) / this.scale;
}
}
// controller
JPanel controllerPanel = new JPanel(new BorderLayout());
MotorControllerPanel controllerTypePanel = new Motor_UnknownGUI();
JComboBox<String> controllerSelect = new JComboBox<String>();
MotorController controller = null;
JCheckBox invert = new JCheckBox("invert");
// power
JPanel powerPanel = new JPanel(new BorderLayout());
private FloatJSlider power = null;
private JLabel powerValue = new JLabel("0.00");
ImageButton stopButton;
ImageButton clockwiseButton;
ImageButton counterclockwiseButton;
// position
JPanel positionPanel = null;
// private JLabel posValue = new JLabel("0.00");
JLabel currentPosition = new JLabel("0");
// TODO - make MotorPanel - for 1 motor - for shared embedded widget
// TODO - stop sign button for panic stop
// TODO - tighten up interfaces
// TODO - DIRECT calls ! - motor & controller HAVE to be on the same
// computer
// TODO - cw ccw buttons enabled
Motor myMotor;
// FIXME put in sub gui
ArrayList<Pin> pinList = null;
public MotorGUI(final String boundServiceName, final GUIService myService, final JTabbedPane tabs) {
super(boundServiceName, myService, tabs);
}
@Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == controllerSelect) {
String newController = (String) controllerSelect.getSelectedItem();
if (newController != null && newController.length() > 0) {
// myService.send(boundServiceName, "setPort", newPort);
ServiceInterface sw = Runtime.getService(newController);
controller = (MotorController) Runtime.getService(newController);
String type = sw.getSimpleName();
// build gui for appropriate motor controller type -
// the gui needs to be able to do a Motor.attach(name, data)
// with appropriate data
String attachGUIName = String.format("org.myrobotlab.control.Motor_%sGUI", type);
controllerPanel.remove(controllerTypePanel);
controllerTypePanel = Reflector.getNewInstance(attachGUIName, new Object[] { myService, boundServiceName, newController });
controllerPanel.add(controllerTypePanel, BorderLayout.CENTER);
// setEnabled(true);
} else {
controllerPanel.remove(controllerTypePanel);
controllerTypePanel = new Motor_UnknownGUI();
controllerPanel.add(controllerTypePanel, BorderLayout.CENTER);
// setEnabled(false);
}
controllerTypePanel.setBorder(BorderFactory.createTitledBorder("type"));
controllerPanel.revalidate();
} else if (source == stopButton) {
power.setValue(0);
}
}
@Override
public void attachGUI() {
subscribe("publishState", "getState", Motor.class);
subscribe("publishChangePos", "onChangePos", Double.class);
myService.send(boundServiceName, "publishState");
}
@Override
public void detachGUI() {
unsubscribe("publishState", "getState", Arduino.class);
unsubscribe("publishChangePos", "onChangePos", Double.class);
}
public void getState(Motor motor) {
myMotor = motor;
setEnabled(motor.isAttached());
// FIXED - can't use a reference - because it changes mid-stream through
// this method
// MotorControllerPanel subpanel =
// ((MotorControllerPanel)controllerTypePanel);
if (motor.isAttached()) {
// !!!!! - This actually fires the (makes a new
// MotorControllerPanel) !!!!!
MotorController mc = (MotorController)motor.getController();
controllerSelect.setSelectedItem(mc.getName());
controllerTypePanel.set(motor);
}
controllerTypePanel.setAttached(motor.isAttached());
if (motor.isInverted()) {
invert.setSelected(true);
} else {
invert.setSelected(false);
}
}
@Override
public void init() {
// controllerPanel begin ------------------
controllerPanel.setBorder(BorderFactory.createTitledBorder("controller"));
List<String> v = Runtime.getServiceNamesFromInterface(MotorController.class);
v.add(0, "");
controllerSelect = new JComboBox(v.toArray());
controllerPanel.add(controllerSelect, BorderLayout.WEST);
controllerPanel.add(controllerTypePanel, BorderLayout.CENTER);
// controllerPanel end ------------------
// powerPanel begin ------------------
powerPanel.setBorder(BorderFactory.createTitledBorder("power"));
JPanel north = new JPanel();
north.add(invert);
north.add(powerValue);
powerPanel.add(north, BorderLayout.NORTH);
counterclockwiseButton = new ImageButton("Motor", "counterclockwise", this);
stopButton = new ImageButton("Motor", "stop", this);
clockwiseButton = new ImageButton("Motor", "clockwise", this);
powerPanel.add(counterclockwiseButton, BorderLayout.WEST);
powerPanel.add(stopButton, BorderLayout.CENTER);
powerPanel.add(clockwiseButton, BorderLayout.EAST);
power = new FloatJSlider(-100, 100, 0, 100);
power.setMajorTickSpacing(25);
// power.setMinorTickSpacing(10);
power.setPaintTicks(true);
power.setPaintLabels(true);
powerPanel.add(power, BorderLayout.SOUTH);
// powerValue.setPreferredSize(new Dimension(100,50));
// powerPanel end ------------------
// positionPanel begin ------------------
positionPanel = new JPanel();
positionPanel.setBorder(BorderFactory.createTitledBorder("position"));
positionPanel.add(new JLabel("current position "));
positionPanel.add(currentPosition);
// positionPanel end ------------------
gc.gridx = 0;
gc.gridy = 0;
gc.fill = GridBagConstraints.HORIZONTAL;
display.add(controllerPanel, gc);
++gc.gridy;
display.add(powerPanel, gc);
++gc.gridy;
display.add(positionPanel, gc);
controllerSelect.addActionListener(this);
power.addChangeListener(this);
// TODO - motor could come into graphics already attached - handle it...
}
public void onChangePos(Double newPos) {
currentPosition.setText(String.format("%3.2f", newPos));
}
public void setEnabled(boolean enable) {
stopButton.setEnabled(enable);
clockwiseButton.setEnabled(enable);
counterclockwiseButton.setEnabled(enable);
power.setEnabled(enable);
invert.setEnabled(enable);
powerValue.setEnabled(enable);
}
@Override
public void stateChanged(ChangeEvent ce) {
Object source = ce.getSource();
if (power == source) {
// powerValue.setText(power.getValue() + "%");
powerValue.setText(String.format("in %3.2f out %3.0f", power.getScaledValue(), myMotor.getPowerMap().calc(power.getScaledValue())));
myService.send(boundServiceName, "move", power.getScaledValue());
}
}
}