import org.armedbear.lisp.Environment; import casa.Agent; import casa.CASAProcess; import casa.LispAccessible; import casa.ML; import casa.Status; import casa.TransientAgent; import casa.abcl.CasaLispOperator; import casa.abcl.ParamsMap; import casa.agentCom.URLDescriptor; import casa.exceptions.URLDescriptorException; import casa.ui.AgentUI; /** * 1. use LISP command line (in casa chat) to do sample moves with the robot * 2. create a LISP accessible java method to (a) be called from (1), and then (b) execute more than 1 LISP command * have extra parameters, e.g. speed, duration, angle * 3. ("sleep" bad) drive robot and then create time event (for queuing) to stop robot (call event.start) * 4. new casa lisp operator (has optional & keyed parameters) * 5. having a request instead of LISP commands */ public class DrEvil extends Agent { public static void main(String[] p) { String cmd = "(let (\n"+ " (trace-tags \"-info,warning,msg,iRobot,-boundSymbols,-policies9,-commitments,-eventqueue,-conversations\")\n"+ " (trace-code 10) ; bit 1=off, 2=on, 4=to-monitor-window, 8=trace-to-file\n"+ " (sleep-time 2) ; time to sleep between starting dependent agents, adjust for slower machines\n"+ // " (outstream \"/dev/tty.ElementSerial-ElementSe\")\n"+ // " (instream \"/dev/tty.ElementSerial-ElementSe\")\n"+ " (outstream \"robot.out\")\n"+ " (instream \"robot.in\")\n"+ " )\n"+ " \n"+ ";; Set the options for the agent running the commandline\n"+ "(agent.options :options.tracing T)\n"+ "(agent.options :options.tracetags trace-tags)\n"+ "\n"+ ";(agent.new-agent \"casa.LAC\" \"ResearchLAC\" 9000 :process \"CURRENT\" :markup \"KQML\" :trace :traceFile :traceTags \"warning,msg,commitments,policies5\")\n"+ ";(sleep 2)\n"+ "\n"+ "(agent.new-agent \"iRobotCreate.iRobotCreate\" \"MiniMe\" 6903 :LAC 9000 :process \"CURRENT\" :trace trace-code :traceTags trace-tags :markup \"KQML\" :outstream outstream :instream instream)\n"+ "(agent.new-agent \"DrEvil\" \"Dr.Evil\" 6909 :LAC 9000 :process \"CURRENT\" :trace trace-code :traceTags trace-tags :markup \"KQML\")\n"+ "\n"+ ") ;let\n"; CASAProcess.main(new String[] {"-LAC", cmd}); } static { createCasaLispOperators( DrEvil.class ); } public DrEvil(ParamsMap params, AgentUI ui) throws Exception { super(params, ui); } /** * VERY simple Lisp command to make a robot turn. * @return */ @LispAccessible(name="DrEvil.poke", help="Makes an iRobot turn around @ fixed speed and angle. All data hard-coded.") public Status poke() { try { abclEval("(agent.tell \"6903\" \"(irobot.drive 20 50)\")",null); Thread.sleep( 5000 ); abclEval("(agent.tell \"6903\" \"(irobot.drive 0 0)\")",null); } catch (InterruptedException e) { e.printStackTrace(); } return new Status( 0 ); } /** * Same Lisp command, but add a parameter for the speed.
* NOTICE: Sleep is bad! * @param speed * @return */ @LispAccessible(name="DrEvil.push", help="Makes an iRobot turn around @ parameter speed & fixed angle. Agent address hard-coded.", arguments={@LispAccessible.Argument(name="speed", help="The speed at which the robot will move [-500,500].")}) public Status push(Integer speed) { try { int aSpeed = speed < -500 ? -500 : speed > 500 ? 500 : speed; abclEval("(agent.tell \"6903\" \"(irobot.drive "+aSpeed+" 2)\")",null); Thread.sleep( 5000 ); abclEval("(agent.tell \"6903\" \"(irobot.drive 0 0)\")",null); } catch (InterruptedException e) { e.printStackTrace(); } return new Status( 0 ); } /** * Same command with parameters for speed and radius. * Fixed the sleep problem by using defer() to queue the stop command into the future. * @param speed * @param radius * @return */ @LispAccessible(name="DrEvil.again", help="Makes an iRobot turn around @ parameter speed & angle. Agent address hard-coded.", arguments={@LispAccessible.Argument(name="speed", help="The speed at which the robot will move [-500,500]."), @LispAccessible.Argument(name="radius", help="The angle at which the robot will rotate.")}) public Status again(Integer speed, Integer radius) { final int aSpeed = speed < -500 ? -500 : speed > 500 ? 500 : speed; final int aAngle = radius; defer(new Runnable() { @Override public void run() { abclEval("(agent.tell \"6903\" \"(irobot.drive "+aSpeed+" "+aAngle+")\")",null); } }); defer(new Runnable() { @Override public void run() { abclEval("(agent.tell \"6903\" \"(irobot.drive 0 0)\")",null); } }, 5000 ); return new Status( 0 ); } /** * Make the parameters key parameters by using an anonymous class of type CasaLispOperator. * Now we can default the speed and radius. */ @SuppressWarnings("unused") private static final CasaLispOperator DREVIL__MORE = new CasaLispOperator("DREVIL.MORE", "\"!Makes an iRobot turn around @ parameter speed & angle. Agent address hard-coded\" " + "&KEY " + "SPEED \"@java.lang.Integer\" \"!The speed (in mm/sec) at which the robot will move [-500, 500]. (optional -- default: 400)\" " + "RADIUS \"@java.lang.Integer\" \"!The radius (in mm) at which the robot will turn. (optional -- default: 2)\" ", TransientAgent.class, DrEvil.class) { @Override public Status execute(TransientAgent agent, ParamsMap params, AgentUI ui, Environment env) { int speed = 40; int angle = 20; if (params.containsKey("SPEED") && params.getJavaObject("SPEED") instanceof Integer) { speed = (Integer) params.getJavaObject("SPEED"); } if (params.containsKey("RADIUS") && params.getJavaObject("RADIUS") instanceof Integer) { angle = (Integer) params.getJavaObject("RADIUS"); } return ((DrEvil)agent).again( speed, angle ); } }; /** * 1. Make the SPEED and RADIUS default to same checking in Java code * 2. Make the iRobot command into an Lisp program to be executed on the other agent * 3. Send the message directly, rather than resorting to Lisp. */ @SuppressWarnings("unused") private static final CasaLispOperator DREVIL__SPEAK = new CasaLispOperator("DREVIL.SPEAK", "\"!Makes an iRobot turn around @ parameter speed & angle. Agent address hard-coded\" " + "&KEY " + "(SPEED 30) \"@java.lang.Integer\" \"!The speed at which the robot will move [-500, 500]. (optional -- default: 400)\" " + "(RADIUS 20) \"@java.lang.Integer\" \"!The angle at which the robot will turn. (optional -- default: 2)\" ", TransientAgent.class, DrEvil.class) { @Override public Status execute(TransientAgent agent, ParamsMap params, AgentUI ui, Environment env) { int speed = (Integer) params.getJavaObject("SPEED"); int angle = (Integer) params.getJavaObject("RADIUS"); Status status; try { URLDescriptor url = URLDescriptor.make(":6903"); String content = "(agent.async '(progn (irobot.drive "+speed+" "+angle+")(sleep 5)(irobot.drive 0 0)))"; status = agent.sendMessage(ML.REQUEST, ML.EXECUTE, url, ML.CONTENT, content ); } catch (URLDescriptorException e) { status = new Status(-6,"Bad URL: "+e.toString()); } return status; } }; }