/** * */ package jp.ac.fit.asura.nao.glue.naimon; import static jp.ac.fit.asura.nao.vision.VisualParam.Boolean.USE_HOUGH; import java.awt.Polygon; import java.awt.Rectangle; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.text.NumberFormat; import java.util.List; import java.util.Queue; import java.util.zip.DeflaterOutputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.vecmath.Matrix3f; import javax.vecmath.Vector3f; import jp.ac.fit.asura.nao.Image; import jp.ac.fit.asura.nao.MotionFrameContext; import jp.ac.fit.asura.nao.RobotContext; import jp.ac.fit.asura.nao.SensorContext; import jp.ac.fit.asura.nao.Switch; import jp.ac.fit.asura.nao.Camera.PixelFormat; import jp.ac.fit.asura.nao.event.VisualEventListener; import jp.ac.fit.asura.nao.localization.Localization; import jp.ac.fit.asura.nao.localization.WorldObject; import jp.ac.fit.asura.nao.localization.WorldObjects; import jp.ac.fit.asura.nao.localization.self.MonteCarloLocalization; import jp.ac.fit.asura.nao.localization.self.SelfLocalization; import jp.ac.fit.asura.nao.localization.self.MonteCarloLocalization.Candidate; import jp.ac.fit.asura.nao.misc.AttributesImpl; import jp.ac.fit.asura.nao.misc.MathUtils; import jp.ac.fit.asura.nao.misc.MatrixUtils; import jp.ac.fit.asura.nao.misc.Pixmap; import jp.ac.fit.asura.nao.misc.XMLWriter; import jp.ac.fit.asura.nao.physical.Robot.Frames; import jp.ac.fit.asura.nao.sensation.FrameState; import jp.ac.fit.asura.nao.sensation.SomaticContext; import jp.ac.fit.asura.nao.strategy.Task; import jp.ac.fit.asura.nao.strategy.permanent.BallTrackingTask; import jp.ac.fit.asura.nao.vision.GCD; import jp.ac.fit.asura.nao.vision.VisualContext; import jp.ac.fit.asura.nao.vision.VisualCortex; import jp.ac.fit.asura.nao.vision.VisualObjects; import jp.ac.fit.asura.nao.vision.perception.BallVisualObject; import jp.ac.fit.asura.nao.vision.perception.BlobVision; import jp.ac.fit.asura.nao.vision.perception.GoalVisualObject; import jp.ac.fit.asura.nao.vision.perception.HoughVision; import jp.ac.fit.asura.nao.vision.perception.RobotVisualObject; import jp.ac.fit.asura.nao.vision.perception.VisualObject; import jp.ac.fit.asura.nao.vision.perception.BlobVision.Blob; import org.apache.log4j.Logger; import org.xml.sax.SAXException; import com.sun.org.apache.xerces.internal.impl.dv.util.Base64; /** * * Naimon2ServletのDOMを使わないバージョン. * * 名前がださいので,問題ないならNaimon2Servletに統合しましょう. * * @author $Author: $ * * @version $Id: $ * */ public class Naimon2Servlet extends HttpServlet { private static final Logger log = Logger.getLogger(Naimon2Servlet.class); private RobotContext robotContext; private static final int BLOB_THRESHOLD_DEFAULT = 10; private int blobThreshold; private boolean isCamImage = false; public Naimon2Servlet(RobotContext context) { this.robotContext = context; } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { log .trace("request :" + req.getRemoteHost() + ":" + req.getRemotePort()); resp.setStatus(HttpServletResponse.SC_OK); resp.setContentType("text/xml; charset=UTF-8"); req.setCharacterEncoding("UTF-8"); String param = null; param = req.getParameter("blob_threshold"); if (param != null) { blobThreshold = Integer.parseInt(param); } else { blobThreshold = BLOB_THRESHOLD_DEFAULT; } param = req.getParameter("camera_image"); if (param != null && param.equals("true")) { isCamImage = true; } else { isCamImage = false; } final PrintWriter pw = resp.getWriter(); final VisualCortex vc = robotContext.getVision(); final Object lock = new Object(); // ルートノードを作成 final XMLWriter w = new XMLWriter(pw); VisualEventListener listener = new VisualEventListener() { @Override public void updateVision(VisualContext context) { try { long beginTime = System.nanoTime(); w.startDocument(); w.startElement("NaimonFrame"); // Visionエレメントを追加 writeVisionElement(w, context); // WorldObjectsエレメントを追加 writeWorldObjectsElement(w, context); // Localizationエレメントを追加 writeLocalizationElement(w, context); // Valuesエレメントを追加 writeValuesElement(w, context); // CamImageエレメントを追加 if (isCamImage) writeCamImageElement(w, context); writeSomaticContextElement(w, context); w.endElement("NaimonFrame"); w.endDocument(); long endTime = System.nanoTime(); long time = endTime - beginTime; // だいたい4.7msぐらいかかる(Core2Duo 2.26 GHz) // DOMだと9.7msぐらいかかる log.trace("elapsedTime " + time / 1000000.0 + " [ms]"); } catch (SAXException e) { log.error("", e); } synchronized (lock) { lock.notifyAll(); } } }; try { vc.addEventListener(listener); synchronized (lock) { lock.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { vc.removeEventListener(listener); } return; } private void writeSomaticContextElement(XMLWriter w, VisualContext context) throws SAXException { w.startElement("SomaticContext"); MotionFrameContext mc = context.getFrameContext().getMotionFrame(); SomaticContext sc = mc.getSomaticContext(); { // 現在の姿勢 w.startElement("Robot"); w.emptyElement("position", new AttributesImpl("y", sc .getBodyHeight())); Matrix3f rot = sc.getBodyPosture(); Vector3f pyr = new Vector3f(); MatrixUtils.rot2pyr(rot, pyr); w.emptyElement("rotation", new AttributesImpl("pitch", pyr.x, "yaw", pyr.y, "roll", pyr.z)); Vector3f com = sc.getCenterOfMass(); w.emptyElement("comPosition", new AttributesImpl("x", com.x, "y", com.y, "z", com.z)); w.endElement("Robot"); } for (Frames f : Frames.values()) { FrameState fs = sc.get(f); w .startElement("FrameState", new AttributesImpl("name", fs .getId())); // bodyPosition Vector3f pos = fs.getBodyPosition(); w.emptyElement("position", new AttributesImpl("x", pos.x, "y", pos.y, "z", pos.z)); // bodyRotation (Pitch-Yaw-Roll表現) Matrix3f rot = fs.getBodyRotation(); Vector3f pyr = new Vector3f(); MatrixUtils.rot2pyr(rot, pyr); w.emptyElement("rotation", new AttributesImpl("pitch", pyr.x, "yaw", pyr.y, "roll", pyr.z)); w.emptyElement("angle", new AttributesImpl("min", fs.getFrame() .getMinAngle(), "max", fs.getFrame().getMaxAngle(), "value", fs.getAngle())); w.endElement("FrameState"); } w.endElement("SomaticContext"); } private void writeCamImageElement(XMLWriter w, VisualContext context) throws SAXException { Image image = context.image; byte[] yvuPlane = new byte[image.getWidth() * image.getHeight() * 3]; if (image.getPixelFormat() == PixelFormat.RGB444) { GCD.rgb2yvu(image.getIntBuffer(), yvuPlane); } else if (image.getPixelFormat() == PixelFormat.YUYV) { GCD.yuyv2yvu(image.getByteBuffer(), yvuPlane); } else { assert false; yvuPlane = null; } Pixmap ppm = new Pixmap(yvuPlane, image.getWidth(), image.getHeight(), 255); String encodedImage; ByteArrayOutputStream bout = new ByteArrayOutputStream(); DeflaterOutputStream dout = new DeflaterOutputStream(bout); try { dout.write(ppm.getData()); dout.close(); } catch (IOException e) { e.printStackTrace(); } encodedImage = Base64.encode(bout.toByteArray()); AttributesImpl attrs = new AttributesImpl(); attrs.addAttribute("width", image.getWidth()); attrs.addAttribute("height", image.getHeight()); attrs.addAttribute("frame", context.getFrameContext().getFrame()); attrs.addAttribute("length", ppm.getData().length); w.startElement("CamImage", attrs); w.characters(encodedImage); w.endElement("CamImage"); } /** * Visionのエレメントノードを構成します * * @param context * @return */ private void writeVisionElement(XMLWriter w, VisualContext context) throws SAXException { w.startElement("Vision"); AttributesImpl gcdAttrs = new AttributesImpl(); gcdAttrs.addAttribute("width", context.image.getWidth()); gcdAttrs.addAttribute("height", context.image.getHeight()); gcdAttrs.addAttribute("length", context.gcdPlane.length); // GCDエレメントを加える w.startElement("GCD", gcdAttrs); // gcdPlaneを圧縮 ByteArrayOutputStream bout = new ByteArrayOutputStream(); DeflaterOutputStream dout = new DeflaterOutputStream(bout); try { dout.write(context.gcdPlane); dout.close(); } catch (IOException e) { e.printStackTrace(); } // Base64エンコードする w.characters(Base64.encode(bout.toByteArray())); // GCDエレメント終わり w.endElement("GCD"); if (context.getParam(USE_HOUGH)) { // USE_HOUGHが有効ならHoughエレメントを加える AttributesImpl houghAttrs = new AttributesImpl(); houghAttrs.addAttribute("width", String .valueOf(HoughVision.RHO_MAX)); houghAttrs.addAttribute("height", String .valueOf(HoughVision.THETA_MAX)); houghAttrs.addAttribute("length", String .valueOf(context.houghPlane.length)); // Houghエレメントを加える w.startElement("Hough", houghAttrs); // houghPlaneを圧縮 ByteArrayOutputStream bout2 = new ByteArrayOutputStream(); DeflaterOutputStream dout2 = new DeflaterOutputStream(bout2); try { dout2.write(context.houghPlane); dout2.close(); } catch (IOException e) { e.printStackTrace(); } // Base64エンコードする w.characters(Base64.encode(bout2.toByteArray())); w.endElement("Hough"); } // Blobsエレメントを加える writeBlobsElement(w, context, GCD.cORANGE, blobThreshold); writeBlobsElement(w, context, GCD.cCYAN, blobThreshold); writeBlobsElement(w, context, GCD.cYELLOW, blobThreshold); writeBlobsElement(w, context, GCD.cRED, blobThreshold); writeBlobsElement(w, context, GCD.cBLUE, blobThreshold); writeBlobsElement(w, context, GCD.cWHITE, blobThreshold); // VisualObjectsエレメントを加える writeVisualObjectsElement(w, context); w.endElement("Vision"); } /** * * @param context * @return */ private void writeLocalizationElement(XMLWriter w, VisualContext context) throws SAXException { w.startElement("Localization"); Localization loc = robotContext.getLocalization(); // World Objects for (WorldObjects e : WorldObjects.values()) { AttributesImpl attrs = new AttributesImpl(); WorldObject wo = loc.get(e); attrs.addAttribute("type", String.valueOf(e.ordinal())); attrs.addAttribute("worldX", String.valueOf(wo.getWorldX())); attrs.addAttribute("worldY", String.valueOf(wo.getWorldY())); attrs.addAttribute("distance", String.valueOf(wo.getDistance())); attrs.addAttribute("heading", String.valueOf(wo.getHeading())); attrs .addAttribute("confidence", String.valueOf(wo .getConfidence())); attrs.addAttribute("yaw", String.valueOf(wo.getYaw())); w.emptyElement("WorldObject", attrs); } // SelfLocalization SelfLocalization self = loc.getSelf(); if (self instanceof MonteCarloLocalization) { MonteCarloLocalization mcl = (MonteCarloLocalization) self; Candidate[] candidates = mcl.getCandidates(); int size = 32; // samples; if (candidates.length < size) size = candidates.length; for (int i = 0; i < size; i++) { Candidate c = candidates[i]; w.emptyElement("Candidates", new AttributesImpl("x", c.x, "y", c.y, "h", c.h, "w", c.w)); } } w.endElement("Localization"); } /** * * @param context * @return * @throws SAXException */ private void writeWorldObjectsElement(XMLWriter w, VisualContext context) throws SAXException { w.startElement("WorldObjects"); Localization loc = robotContext.getLocalization(); for (WorldObjects key : WorldObjects.values()) { WorldObject wo = loc.get(key); NumberFormat f = NumberFormat.getInstance(); f.setMaximumFractionDigits(2); AttributesImpl attrs = new AttributesImpl(7); attrs.addAttribute("name", wo.getType()); attrs.addAttribute("X", wo.getX()); attrs.addAttribute("Y", wo.getY()); attrs.addAttribute("Heading", f.format(wo.getHeading())); attrs.addAttribute("Yaw", f.format(wo.getYaw())); attrs.addAttribute("Confidence", wo.getConfidence()); attrs.addAttribute("Distance", wo.getDistance()); w.emptyElement("Item", attrs); } w.endElement("WorldObjects"); } /** * * @param context * @return */ private void writeVisualObjectsElement(XMLWriter w, VisualContext context) throws SAXException { w.startElement("VisualObjects"); for (VisualObjects key : VisualObjects.values()) { VisualObject vo = context.get(key); NumberFormat f = NumberFormat.getInstance(); f.setMaximumFractionDigits(2); AttributesImpl attrs = new AttributesImpl(); attrs.addAttribute("name", vo.getType().toString()); attrs.addAttribute("CenterX", String.valueOf(vo.center.x)); attrs.addAttribute("CenterY", String.valueOf(vo.center.y)); attrs.addAttribute("AngleX", f.format(MathUtils .toDegrees(vo.angle.x))); attrs.addAttribute("AngleY", f.format(MathUtils .toDegrees(vo.angle.y))); attrs.addAttribute("RobotAngleX", f.format(MathUtils .toDegrees(vo.robotAngle.x))); attrs.addAttribute("RobotAngleY", f.format(MathUtils .toDegrees(vo.robotAngle.y))); attrs.addAttribute("Confidence", String.valueOf(vo.confidence)); if (key == VisualObjects.Ball) { attrs .addAttribute("Distance", ((BallVisualObject) vo).distance); } else if (key == VisualObjects.RedNao || key == VisualObjects.BlueNao) { attrs.addAttribute("Distance", ((RobotVisualObject) vo).distance); } else if (key == VisualObjects.BlueGoal || key == VisualObjects.YellowGoal) { attrs .addAttribute("Distance", ((GoalVisualObject) vo).distance); } w.startElement("Item", attrs); if (key == VisualObjects.BlueGoal || key == VisualObjects.YellowGoal) { Polygon p = vo.polygon; for (int i = 0; i < p.npoints; i++) { w.emptyElement("Polygon", new AttributesImpl("x", p.xpoints[i], "y", p.ypoints[i])); } } if (vo.confidence > 0) { Rectangle r = vo.area; w.emptyElement("Polygon", new AttributesImpl("x", r.x, "y", r.y, "width", r.width, "height", r.height)); } w.endElement("Item"); } w.endElement("VisualObjects"); } /** * * @param context * @param colorIndex * @param threshold * @return * @throws SAXException */ private void writeBlobsElement(XMLWriter w, VisualContext context, byte colorIndex, int threshold) throws SAXException { w.startElement("Blobs", new AttributesImpl("colorIndex", colorIndex, "threshold", threshold)); List<Blob> list = context.blobVision.findBlobs(colorIndex, BlobVision.MAX_BLOBS, threshold); for (Blob b : list) { w.emptyElement("Blob", new AttributesImpl("xmin", b.xmin, "xmax", b.xmax, "ymin", b.ymin, "ymax", b.ymax)); } w.endElement("Blobs"); } /** * * @return * @throws SAXException */ private void writeValuesElement(XMLWriter w, VisualContext context) throws SAXException { w.startElement("Values"); // Frame Count w.emptyElement("Item", new AttributesImpl("name", "FrameCount", "value", context.getFrameContext().getFrame())); // Robot ID w.emptyElement("Item", new AttributesImpl("name", "RobotID", "value", robotContext.getRobotId())); // Game status w.emptyElement("Item", new AttributesImpl("name", "GameStatus", "value", robotContext.getStrategy().getGameState())); // Penalized w.emptyElement("Item", new AttributesImpl("name", "Penalized", "value", robotContext.getStrategy().isPenalized())); // Current team color w.emptyElement("Item", new AttributesImpl("name", "Team", "value", robotContext.getStrategy().getTeam())); // Current role w.emptyElement("Item", new AttributesImpl("name", "Role", "value", robotContext.getStrategy().getRole())); // Current Scheduler w.emptyElement("Item", new AttributesImpl("name", "Scheduler", "value", robotContext.getStrategy().getScheduler().getName())); // Current task String task = "N/A"; try { task = robotContext.getStrategy().getScheduler().getCurrentTask() .getName(); } catch (NullPointerException e) { } w.emptyElement("Item", new AttributesImpl("name", "CurrentTask", "value", task)); // Current task TTL int ttl = -1; try { ttl = robotContext.getStrategy().getScheduler().getTTL(); } catch (NullPointerException e) { } w.emptyElement("Item", new AttributesImpl("name", "TimeToLive", "value", ttl)); // NextTask task = "N/A"; try { Queue<Task> queue = robotContext.getStrategy().getScheduler() .getQueue(); if (queue.size() > 0) { task = queue.peek().getName(); } } catch (NullPointerException e) { } w.emptyElement("Item", new AttributesImpl("name", "NextTask", "value", task)); // Current tracking mode BallTrackingTask tracking = (BallTrackingTask) robotContext .getStrategy().getTaskManager().find("BallTracking"); if (tracking != null) { w.emptyElement("Item", new AttributesImpl("name", "TrackingMode", "value", tracking.getModeName())); } // Current Motion String motion = "null"; try { motion = robotContext.getMotor().getCurrentMotion().getName(); } catch (NullPointerException e) { } w.emptyElement("Item", new AttributesImpl("name", "CurrentMotion", "value", motion)); // Sensors SensorContext sensor = context.getFrameContext().getMotionFrame() .getSensorContext(); for (Switch sw : Switch.values()) { w.emptyElement("Item", new AttributesImpl("name", sw, "value", sensor.getSwitch(sw))); } w.endElement("Values"); } }