diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/robot/RRMapDraw.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/robot/RRMapDraw.java index 851215bc1..2ba7c20eb 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/robot/RRMapDraw.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/robot/RRMapDraw.java @@ -33,6 +33,10 @@ import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import javax.imageio.ImageIO; @@ -78,8 +82,8 @@ public class RRMapDraw { private static final Color ROOM9 = new Color(0xFc, 0xD4, 0x51); private static final Color ROOM10 = new Color(72, 201, 176); private static final Color ROOM11 = new Color(84, 153, 199); - private static final Color ROOM12 = new Color(133, 193, 233); - private static final Color ROOM13 = new Color(245, 176, 65); + private static final Color ROOM12 = new Color(255, 213, 209); + private static final Color ROOM13 = new Color(228, 228, 215); private static final Color ROOM14 = new Color(82, 190, 128); private static final Color ROOM15 = new Color(72, 201, 176); private static final Color ROOM16 = new Color(165, 105, 189); @@ -132,6 +136,7 @@ public class RRMapDraw { */ private void drawMap(Graphics2D g2d, float scale) { Stroke stroke = new BasicStroke(1.1f * scale); + Set roomIds = new HashSet(); g2d.setStroke(stroke); for (int y = 0; y < rmfp.getImgHeight() - 1; y++) { for (int x = 0; x < rmfp.getImgWidth() + 1; x++) { @@ -160,7 +165,8 @@ public class RRMapDraw { g2d.setColor(Color.BLACK); break; case 7: - g2d.setColor(ROOM_COLORS[Math.round(mapId / 2)]); + g2d.setColor(ROOM_COLORS[mapId % 15]); + roomIds.add(mapId); multicolor = true; break; default: @@ -173,6 +179,13 @@ public class RRMapDraw { g2d.draw(new Line2D.Float(xPos, yP, xPos, yP)); } } + if (logger.isDebugEnabled() && roomIds.size() > 0) { + StringBuilder sb = new StringBuilder(); + for (Integer r : roomIds) { + sb.append(" " + r.toString()); + } + logger.debug("Identified rooms in map:{}", sb.toString()); + } } /** @@ -275,20 +288,44 @@ public class RRMapDraw { g2d.setColor(COLOR_CHARGER_HALO); final float chargerX = toXCoord(rmfp.getChargerX()) * scale; final float chargerY = toYCoord(rmfp.getChargerY()) * scale; - drawCircle(g2d, chargerX, chargerY, radius); + drawCircle(g2d, chargerX, chargerY, radius, false); drawCenteredImg(g2d, scale / 8, "charger.png", chargerX, chargerY); radius = 3 * scale; g2d.setColor(COLOR_ROBO); final float roboX = toXCoord(rmfp.getRoboX()) * scale; final float roboY = toYCoord(rmfp.getRoboY()) * scale; - drawCircle(g2d, roboX, roboY, radius); + drawCircle(g2d, roboX, roboY, radius, false); if (scale > 1.5) { drawCenteredImg(g2d, scale / 15, "robo.png", roboX, roboY); } } - private void drawCircle(Graphics2D g2d, float x, float y, float radius) { - g2d.draw(new Ellipse2D.Double(x - radius, y - radius, 2.0 * radius, 2.0 * radius)); + private void drawObstacles(Graphics2D g2d, float scale) { + float radius = 2 * scale; + Stroke stroke = new BasicStroke(3 * scale); + g2d.setStroke(stroke); + g2d.setColor(Color.MAGENTA); + + Map> obstacleMap = rmfp.getObstacles(); + for (ArrayList obstacles : obstacleMap.values()) { + obstacles.forEach(obstacle -> { + final float obstacleX = toXCoord(obstacle[0]) * scale; + final float obstacleY = toYCoord(obstacle[1]) * scale; + drawCircle(g2d, obstacleX, obstacleY, radius, true); + if (scale > 1.0) { + drawCenteredImg(g2d, scale / 3, "obstacle-" + obstacle[2] + ".png", obstacleX, obstacleY + 15); + } + }); + } + } + + private void drawCircle(Graphics2D g2d, float x, float y, float radius, boolean fill) { + Ellipse2D.Double circle = new Ellipse2D.Double(x - radius, y - radius, 2.0 * radius, 2.0 * radius); + if (fill) { + g2d.fill(circle); + } else { + g2d.draw(circle); + } } private void drawCenteredImg(Graphics2D g2d, float scale, String imgFile, float x, float y) { @@ -296,10 +333,10 @@ public class RRMapDraw { try { if (image != null) { BufferedImage addImg = ImageIO.read(image); - int xpos = Math.round(x - (addImg.getWidth() / 2 * scale)); - int ypos = Math.round(y - (addImg.getHeight() / 2 * scale)); + int xpos = Math.round(x + (addImg.getWidth() / 2 * scale)); + int ypos = Math.round(y + (addImg.getHeight() / 2 * scale)); AffineTransform at = new AffineTransform(); - at.scale(scale, scale); + at.scale(-scale, -scale); AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR); g2d.drawImage(addImg, scaleOp, xpos, ypos); } else { @@ -410,6 +447,7 @@ public class RRMapDraw { drawPath(g2d, scale); drawRobo(g2d, scale); drawGoTo(g2d, scale); + drawObstacles(g2d, scale); g2d = bi.createGraphics(); drawOpenHabRocks(g2d, width, height, scale); return bi; diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/robot/RRMapFileParser.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/robot/RRMapFileParser.java index 129c3c316..a26cd99d0 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/robot/RRMapFileParser.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/robot/RRMapFileParser.java @@ -52,6 +52,11 @@ public class RRMapFileParser { public static final int BLOCKS = 11; public static final int MFBZS_AREA = 12; public static final int OBSTACLES = 13; + public static final int IGNORED_OBSTACLES = 14; + public static final int OBSTACLES2 = 15; + public static final int IGNORED_OBSTACLES2 = 16; + public static final int CARPET_MAP = 17; + public static final int DIGEST = 1024; public static final int HEADER = 0x7272; @@ -84,8 +89,9 @@ public class RRMapFileParser { private Map> areas = new HashMap<>(); private ArrayList walls = new ArrayList<>(); private ArrayList zones = new ArrayList<>(); - private ArrayList obstacles = new ArrayList<>(); + private Map> obstacles = new HashMap<>(); private byte[] blocks = new byte[0]; + private int[] carpetMap = {}; private final Logger logger = LoggerFactory.getLogger(RRMapFileParser.class); @@ -192,12 +198,62 @@ public class RRMapFileParser { areas.put(Integer.valueOf(blocktype & 0xFF), area); break; case OBSTACLES: + case IGNORED_OBSTACLES: int obstaclePairs = getUInt16(header, 0x08); + ArrayList obstacle = new ArrayList<>(); for (int obstaclePair = 0; obstaclePair < obstaclePairs; obstaclePair++) { int x0 = getUInt16(data, obstaclePair * 5 + 0); int y0 = getUInt16(data, obstaclePair * 5 + 2); int u = data[obstaclePair * 5 + 0] & 0xFF; - obstacles.add(new int[] { x0, y0, u }); + obstacle.add(new int[] { x0, y0, u }); + } + obstacles.put(Integer.valueOf(blocktype & 0xFF), obstacle); + break; + case OBSTACLES2: + int obstacle2Pairs = getUInt16(header, 0x08); + if (obstacle2Pairs == 0) { + break; + } + int obstacleDataLenght = blockDataLength / obstacle2Pairs; + logger.trace("block 15 records lenght: {}", obstacleDataLenght); + ArrayList obstacle2 = new ArrayList<>(); + for (int obstaclePair = 0; obstaclePair < obstacle2Pairs; obstaclePair++) { + int x0 = getUInt16(data, obstaclePair * obstacleDataLenght + 0); + int y0 = getUInt16(data, obstaclePair * obstacleDataLenght + 2); + int u0 = getUInt16(data, obstaclePair * obstacleDataLenght + 4); + int u1 = getUInt16(data, obstaclePair * obstacleDataLenght + 6); + int u2 = getUInt32LE(data, obstaclePair * obstacleDataLenght + 8); + if (obstacleDataLenght == 28) { + if ((data[obstaclePair * obstacleDataLenght + 12] & 0xFF) == 0) { + logger.trace("obstacle with photo: No text"); + } else { + byte[] txt = getBytes(data, obstaclePair * obstacleDataLenght + 12, 16); + logger.trace("obstacle with photo: {}", new String(txt)); + } + obstacle2.add(new int[] { x0, y0, u0, u1, u2 }); + } else { + int u3 = getUInt32LE(data, obstaclePair * obstacleDataLenght + 12); + obstacle2.add(new int[] { x0, y0, u0, u1, u2, u3 }); + logger.trace("obstacle without photo."); + } + } + obstacles.put(Integer.valueOf(blocktype & 0xFF), obstacle2); + break; + case IGNORED_OBSTACLES2: + int ignoredObstaclePairs = getUInt16(header, 0x08); + ArrayList ignoredObstacle = new ArrayList<>(); + for (int obstaclePair = 0; obstaclePair < ignoredObstaclePairs; obstaclePair++) { + int x0 = getUInt16(data, obstaclePair * 6 + 0); + int y0 = getUInt16(data, obstaclePair * 6 + 2); + int u = getUInt16(data, obstaclePair * 6 + 4); + ignoredObstacle.add(new int[] { x0, y0, u }); + } + obstacles.put(Integer.valueOf(blocktype & 0xFF), ignoredObstacle); + break; + case CARPET_MAP: + carpetMap = new int[blockDataLength]; + for (int carpetNode = 0; carpetNode < blockDataLength; carpetNode++) { + carpetMap[carpetNode] = data[carpetNode] & 0xFF; } break; case BLOCKS: @@ -285,7 +341,10 @@ public class RRMapFileParser { printAreaDetails(walls, pw); pw.printf("Zones:\t%d\r\n", zones.size()); printAreaDetails(zones, pw); - pw.printf("Obstacles:\t%d\r\n", obstacles.size()); + for (Integer obstacleType : obstacles.keySet()) { + pw.printf("Obstacles Type (%d):\t%d\r\n", obstacleType, obstacles.get(obstacleType).size()); + printObstacleDetails(obstacles.get(obstacleType), pw); + } pw.printf("Blocks:\t%d\r\n", blocks.length); pw.print("Paths:"); for (Integer p : pathsDetails.keySet()) { @@ -301,7 +360,7 @@ public class RRMapFileParser { private void printAreaDetails(ArrayList areas, PrintWriter pw) { areas.forEach(area -> { - pw.printf("\tArea coordinates:"); + pw.print("\tArea coordinates:"); for (int i = 0; i < area.length; i++) { pw.printf("\t%.0f", area[i]); } @@ -309,6 +368,16 @@ public class RRMapFileParser { }); } + private void printObstacleDetails(ArrayList obstacle, PrintWriter pw) { + obstacle.forEach(area -> { + pw.print("\tObstacle coordinates:"); + for (int i = 0; i < area.length; i++) { + pw.printf("\t%d", area[i]); + } + pw.println(); + }); + } + /** * Compute SHA-1 hash value for the byte array * @@ -416,11 +485,15 @@ public class RRMapFileParser { return areas; } - public ArrayList getObstacles() { + public Map> getObstacles() { return obstacles; } public byte[] getBlocks() { return blocks; } + + public final int[] getCarpetMap() { + return carpetMap; + } }