package org.myrobotlab.service; import java.util.HashMap; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import org.myrobotlab.framework.Service; import org.myrobotlab.framework.ServiceType; import org.myrobotlab.logging.Level; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.Logging; import org.myrobotlab.logging.LoggingFactory; import org.myrobotlab.service.interfaces.ServiceInterface; import org.slf4j.Logger; /** * Keyboard - The keyboard service will track keys that are pressed so they can * be used as input to other services via the addKeyListener(Service) call. * * */ public class Keyboard extends Service { public class Command { public String name; public String method; public Object[] params; Command(String name, String method, Object[] params) { this.name = name; this.method = method; this.params = params; } } private static final long serialVersionUID = 1L; public final static Logger log = LoggerFactory.getLogger(Keyboard.class); // TODO - needs capability to re-map keys // FIXME add to Service HashMap<String, Command> commands = null; HashMap<String, String> remap = new HashMap<String, String>(); transient BlockingQueue<String> blockingData = new LinkedBlockingQueue<String>(); boolean isBlocking = false; public static void main(String[] args) { LoggingFactory.init(Level.INFO); Keyboard keyboard = (Keyboard) Runtime.start("keyboard", "Keyboard"); keyboard.stopService(); } public Keyboard(String n) { super(n); } // TODO - should this be in Service ????? public void addCommand(String actionPhrase, String name, String method, Object... params) { if (commands == null) { commands = new HashMap<String, Command>(); } commands.put(actionPhrase, new Command(name, method, params)); } /** * this method is what other services would use to subscribe to keyboard * events * * @param service */ public void addKeyListener(Service service) { addListener("publishKey", service.getName(), "onKey"); } public void addKeyListener(String serviceName) { ServiceInterface s = Runtime.getService(serviceName); addKeyListener((Service) s); } public void clearMappings() { remap.clear(); } /** * This method will be called by graphic components from here it will invoke * the MRL pub/sub publishKey from which other services may listen for key * events * * @param key * @return */ public String keyCommand(String key) { log.info(key); if (commands != null && commands.containsKey(key)) { Command currentCommand = commands.get(key); send(currentCommand.name, currentCommand.method, currentCommand.params); } if (isBlocking) { blockingData.add(key); } if (remap.containsKey(key)) { invoke("publishKey", remap.get(key)); } else { invoke("publishKey", key); } return key; } /** * a onKey event handler for testing purposes only * * @param key * @return */ public String onKey(String key) { log.info(String.format("onKey [%s]", key)); return key; } /** * internal publishing point - private ? * * @param key */ public String publishKey(String key) { return key; } public String readKey() { try { isBlocking = true; String ret = blockingData.take(); isBlocking = false; return ret; } catch (Exception e) { Logging.logError(e); } isBlocking = false; return null; } public void reMap(String from, String to) { remap.put(from, to); } /** * This static method returns all the details of the class without it having * to be constructed. It has description, categories, dependencies, and peer * definitions. * * @return ServiceType - returns all the data * */ static public ServiceType getMetaData() { ServiceType meta = new ServiceType(Keyboard.class.getCanonicalName()); meta.addDescription("keyboard interface"); meta.addCategory("control"); return meta; } }