package org.openpnp.spi.base; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javax.swing.Icon; import org.openpnp.model.AbstractModelObject; import org.openpnp.model.LengthUnit; import org.openpnp.model.Location; import org.openpnp.spi.Actuator; import org.openpnp.spi.Camera; import org.openpnp.spi.Feeder; import org.openpnp.spi.Head; import org.openpnp.spi.Machine; import org.openpnp.spi.MachineListener; import org.openpnp.spi.Signaler; import org.openpnp.util.IdentifiableList; import org.simpleframework.xml.Attribute; import org.simpleframework.xml.Element; import org.simpleframework.xml.ElementList; import org.simpleframework.xml.core.Commit; import com.google.common.util.concurrent.FutureCallback; public abstract class AbstractMachine extends AbstractModelObject implements Machine { /** * History: * * Note: Can't actually use the @Version annotation because of a bug in SimpleXML. See * http://sourceforge.net/p/simple/mailman/message/27887562/ * * 1.0: Initial revision. 1.1: Added jobProcessors Map and deprecated JobProcesor and * JobPlanner. */ public enum State { ERROR } @ElementList protected IdentifiableList<Head> heads = new IdentifiableList<>(); @ElementList(required = false) protected IdentifiableList<Signaler> signalers = new IdentifiableList<>(); @ElementList(required = false) protected IdentifiableList<Feeder> feeders = new IdentifiableList<>(); @ElementList(required = false) protected IdentifiableList<Camera> cameras = new IdentifiableList<>(); @ElementList(required = false) protected IdentifiableList<Actuator> actuators = new IdentifiableList<>(); @Element(required = false) protected Location discardLocation = new Location(LengthUnit.Millimeters); @Attribute(required = false) protected double speed = 1.0D; protected Set<MachineListener> listeners = Collections.synchronizedSet(new HashSet<>()); protected ThreadPoolExecutor executor; protected AbstractMachine() {} @SuppressWarnings("unused") @Commit private void commit() { for (Head head : heads) { head.setMachine(this); } } @Override public List<Head> getHeads() { return Collections.unmodifiableList(heads); } @Override public Head getHead(String id) { return heads.get(id); } @Override public List<Signaler> getSignalers() { return Collections.unmodifiableList(signalers); } @Override public Signaler getSignaler(String id) { return signalers.get(id); } @Override public Signaler getSignalerByName(String name) { for (Signaler signaler : signalers) { if (signaler.getName().equals(name)) { return signaler; } } return null; } @Override public List<Feeder> getFeeders() { return Collections.unmodifiableList(feeders); } @Override public Feeder getFeeder(String id) { return feeders.get(id); } @Override public List<Camera> getCameras() { return Collections.unmodifiableList(cameras); } @Override public Camera getCamera(String id) { return cameras.get(id); } @Override public List<Actuator> getActuators() { return Collections.unmodifiableList(actuators); } @Override public Actuator getActuator(String id) { return actuators.get(id); } @Override public Actuator getActuatorByName(String name) { for (Actuator actuator : actuators) { if (actuator.getName().equals(name)) { return actuator; } } return null; } @Override public Feeder getFeederByName(String name) { for (Feeder feeder : feeders) { if (feeder.getName().equals(name)) { return feeder; } } return null; } @Override public void home() throws Exception { for (Head head : heads) { head.home(); } } @Override public void addListener(MachineListener listener) { listeners.add(listener); } @Override public void removeListener(MachineListener listener) { listeners.remove(listener); } @Override public void addFeeder(Feeder feeder) throws Exception { feeders.add(feeder); fireIndexedPropertyChange("feeders", feeders.size() - 1, null, feeder); } @Override public void removeFeeder(Feeder feeder) { int index = feeders.indexOf(feeder); if (feeders.remove(feeder)) { fireIndexedPropertyChange("feeders", index, feeder, null); } } @Override public void addCamera(Camera camera) throws Exception { camera.setHead(null); cameras.add(camera); fireIndexedPropertyChange("cameras", cameras.size() - 1, null, camera); } @Override public void removeCamera(Camera camera) { int index = cameras.indexOf(camera); if (cameras.remove(camera)) { fireIndexedPropertyChange("cameras", index, camera, null); } } @Override public void addActuator(Actuator actuator) throws Exception { actuator.setHead(null); actuators.add(actuator); fireIndexedPropertyChange("actuators", actuators.size() - 1, null, actuator); } @Override public void removeActuator(Actuator actuator) { int index = actuators.indexOf(actuator); if (actuators.remove(actuator)) { fireIndexedPropertyChange("actuators", index, actuator, null); } } public void fireMachineHeadActivity(Head head) { for (MachineListener listener : listeners) { listener.machineHeadActivity(this, head); } } public void fireMachineEnabled() { for (MachineListener listener : listeners) { listener.machineEnabled(this); } } public void fireMachineEnableFailed(String reason) { for (MachineListener listener : listeners) { listener.machineEnableFailed(this, reason); } } public void fireMachineDisabled(String reason) { for (MachineListener listener : listeners) { listener.machineDisabled(this, reason); } } public void fireMachineDisableFailed(String reason) { for (MachineListener listener : listeners) { listener.machineDisableFailed(this, reason); } } public void fireMachineBusy(boolean busy) { for (MachineListener listener : listeners) { listener.machineBusy(this, busy); } } @Override public Icon getPropertySheetHolderIcon() { // TODO Auto-generated method stub return null; } @Override public Future<Object> submit(Runnable runnable) { return submit(Executors.callable(runnable)); } @Override public <T> Future<T> submit(Callable<T> callable) { return submit(callable, null); } @Override public <T> Future<T> submit(final Callable<T> callable, final FutureCallback<T> callback) { return submit(callable, callback, false); } @Override public <T> Future<T> submit(final Callable<T> callable, final FutureCallback<T> callback, final boolean ignoreEnabled) { synchronized (this) { if (executor == null || executor.isShutdown()) { executor = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); } } Callable<T> wrapper = new Callable<T>() { public T call() throws Exception { // TODO: lock driver // Notify listeners that the machine is now busy fireMachineBusy(true); // Call the task, storing the result and exception if any T result = null; Exception exception = null; try { if (!ignoreEnabled && !isEnabled()) { throw new Exception("Machine has not been started."); } result = callable.call(); } catch (Exception e) { exception = e; } // If there was an error cancel all pending tasks. if (exception != null) { executor.shutdownNow(); } // If a callback was supplied, call it with the results if (callback != null) { if (exception != null) { callback.onFailure(exception); } else { callback.onSuccess(result); } } // TODO: unlock driver // If no more tasks are scheduled notify listeners that // the machine is no longer busy if (executor.getQueue().isEmpty()) { fireMachineBusy(false); } // Finally, fulfill the Future by either throwing the // exception or returning the result. if (exception != null) { throw exception; } return result; } }; return executor.submit(wrapper); } @Override public Head getDefaultHead() throws Exception { List<Head> heads = getHeads(); if (heads == null || heads.isEmpty()) { throw new Exception("No default head available."); } return heads.get(0); } public Location getDiscardLocation() { return discardLocation; } public void setDiscardLocation(Location discardLocation) { this.discardLocation = discardLocation; } @Override public void setSpeed(double speed) { this.speed = speed; } @Override public double getSpeed() { return speed; } }