/*
* Copyright (C) 2011 Jason von Nieda <jason@vonnieda.org>
*
* This file is part of OpenPnP.
*
* OpenPnP 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 3 of the
* License, or (at your option) any later version.
*
* OpenPnP is distributed in the hope that it will be useful, 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.
*
* You should have received a copy of the GNU General Public License along with OpenPnP. If not, see
* <http://www.gnu.org/licenses/>.
*
* For more information about OpenPnP visit http://openpnp.org
*/
package org.openpnp.machine.reference.driver;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Locale;
import javax.swing.Action;
import javax.swing.Icon;
import org.openpnp.gui.support.PropertySheetWizardAdapter;
import org.openpnp.gui.support.Wizard;
import org.openpnp.machine.reference.ReferenceActuator;
import org.openpnp.machine.reference.ReferenceDriver;
import org.openpnp.machine.reference.ReferenceHead;
import org.openpnp.machine.reference.ReferenceHeadMountable;
import org.openpnp.machine.reference.ReferenceNozzle;
import org.openpnp.machine.reference.ReferencePasteDispenser;
import org.openpnp.model.LengthUnit;
import org.openpnp.model.Location;
import org.openpnp.spi.Head;
import org.openpnp.spi.PropertySheetHolder;
import org.pmw.tinylog.Logger;
import org.simpleframework.xml.Attribute;
public class SimulatorDriver implements ReferenceDriver {
@Attribute(required = false)
private double feedRateMmPerMinute;
private HashMap<Head, Location> headLocations = new HashMap<>();
private boolean enabled;
private Socket socket;
private DataInputStream in;
private PrintStream out;
public SimulatorDriver() throws Exception {
connect();
}
/**
* Gets the Location object being tracked for a specific Head. This is the absolute coordinates
* of a virtual Head on the machine.
*
* @param head
* @return
*/
protected Location getHeadLocation(Head head) {
Location l = headLocations.get(head);
if (l == null) {
l = new Location(LengthUnit.Millimeters, 0, 0, 0, 0);
setHeadLocation(head, l);
}
return l;
}
protected void setHeadLocation(Head head, Location l) {
headLocations.put(head, l);
}
@Override
public void home(ReferenceHead head) throws Exception {
Logger.debug("home()");
checkEnabled();
send("h");
setHeadLocation(head, getHeadLocation(head).derive(0.0, 0.0, 0.0, 0.0));
}
/**
* Return the Location of a specific ReferenceHeadMountable on the machine. We get the
* coordinates for the Head the object is attached to, and then we add the offsets assigned to
* the object to make the coordinates correct for that object.
*/
@Override
public Location getLocation(ReferenceHeadMountable hm) {
return getHeadLocation(hm.getHead()).add(hm.getHeadOffsets());
}
/**
* Commands the driver to move the given ReferenceHeadMountable to the specified Location at the
* given speed. Please see the comments for this method in the code for some important
* considerations when writing your own driver.
*/
@Override
public void moveTo(ReferenceHeadMountable hm, Location location, double speed)
throws Exception {
Logger.debug("moveTo({}, {}, {})", hm, location, speed);
checkEnabled();
// Subtract the offsets from the incoming Location. This converts the
// offset coordinates to driver / absolute coordinates.
location = location.subtract(hm.getHeadOffsets());
// Convert the Location to millimeters, since that's the unit that
// this driver works in natively.
location = location.convertToUnits(LengthUnit.Millimeters);
// Get the current location of the Head that we'll move
Location hl = getHeadLocation(hm.getHead());
String movable;
if (hm.toString().equals("N1")) {
movable = "Nozzle1";
}
else if (hm.toString().equals("N2")) {
movable = "Nozzle2";
}
else if (hm.toString().contains("Camera")) {
movable = "Camera";
}
else if (hm.toString().equals("A1")) {
movable = "Actuator";
}
else {
throw new Exception("Don't know what " + hm.toString() + " is.");
}
send(String.format(Locale.US, "m,%s,%f,%f,%f,%f", movable, location.getX(), location.getY(),
location.getZ(), location.getRotation()));
// Now that movement is complete, update the stored Location to the new
// Location, unless the incoming Location specified an axis with a value
// of NaN. NaN is interpreted to mean "Don't move this axis" so we don't
// update the value, either.
hl = hl.derive(Double.isNaN(location.getX()) ? null : location.getX(),
Double.isNaN(location.getY()) ? null : location.getY(),
Double.isNaN(location.getZ()) ? null : location.getZ(),
Double.isNaN(location.getRotation()) ? null : location.getRotation());
setHeadLocation(hm.getHead(), hl);
}
@Override
public void pick(ReferenceNozzle nozzle) throws Exception {
Logger.debug("pick({})", nozzle);
checkEnabled();
if (feedRateMmPerMinute > 0) {
Thread.sleep(500);
}
}
@Override
public void place(ReferenceNozzle nozzle) throws Exception {
Logger.debug("place({})", nozzle);
checkEnabled();
if (feedRateMmPerMinute > 0) {
Thread.sleep(500);
}
}
@Override
public void actuate(ReferenceActuator actuator, double value) throws Exception {
Logger.debug("actuate({}, {})", actuator, value);
checkEnabled();
if (feedRateMmPerMinute > 0) {
Thread.sleep(500);
}
}
@Override
public void actuate(ReferenceActuator actuator, boolean on) throws Exception {
Logger.debug("actuate({}, {})", actuator, on);
checkEnabled();
if (feedRateMmPerMinute > 0) {
Thread.sleep(500);
}
}
@Override
public void dispense(ReferencePasteDispenser dispenser, Location startLocation,
Location endLocation, long dispenseTimeMilliseconds) throws Exception {}
@Override
public void setEnabled(boolean enabled) throws Exception {
Logger.debug("setEnabled({})", enabled);
this.enabled = enabled;
}
private void checkEnabled() throws Exception {
if (!enabled) {
throw new Exception("Driver is not yet enabled!");
}
}
// TODO: This reconnect stuff totally doesn't work
private void connect() {
if (socket == null || !socket.isConnected()) {
System.out.println("Connecting to simulator...");
}
while (socket == null || !socket.isConnected()) {
try {
socket = new Socket("localhost", 9037);
in = new DataInputStream(socket.getInputStream());
out = new PrintStream(socket.getOutputStream());
System.out.println("Connected!");
}
catch (Exception e) {
e.printStackTrace();
}
}
}
private void send(String s) {
try {
connect();
out.print(s);
out.print("\n");
String line = in.readLine();
if (!line.trim().equals("ok")) {
throw new Exception("Didn't expect: " + line);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
@Override
public Wizard getConfigurationWizard() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getPropertySheetHolderTitle() {
return getClass().getSimpleName();
}
@Override
public PropertySheetHolder[] getChildPropertySheetHolders() {
// TODO Auto-generated method stub
return null;
}
@Override
public PropertySheet[] getPropertySheets() {
return new PropertySheet[] {new PropertySheetWizardAdapter(getConfigurationWizard())};
}
@Override
public Action[] getPropertySheetHolderActions() {
// TODO Auto-generated method stub
return null;
}
@Override
public Icon getPropertySheetHolderIcon() {
// TODO Auto-generated method stub
return null;
}
@Override
public void close() throws IOException {
// TODO Auto-generated method stub
}
}