hu.elte.ik.robotika.futar.vertx.backend.verticle.HTTPVerticle.java Source code

Java tutorial

Introduction

Here is the source code for hu.elte.ik.robotika.futar.vertx.backend.verticle.HTTPVerticle.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package hu.elte.ik.robotika.futar.vertx.backend.verticle;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.awt.Point;

import org.apache.http.HttpStatus;

import hu.elte.ik.robotika.futar.vertx.backend.auth.SimpleAuthHandler;
import hu.elte.ik.robotika.futar.vertx.backend.auth.SimpleLoginHandler;
import hu.elte.ik.robotika.futar.vertx.backend.domain.User;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.ServerWebSocket;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.ext.auth.AuthProvider;
import io.vertx.ext.auth.shiro.ShiroAuth;
import io.vertx.ext.auth.shiro.ShiroAuthRealmType;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CookieHandler;
import io.vertx.ext.web.handler.SessionHandler;
import io.vertx.ext.web.handler.StaticHandler;
import io.vertx.ext.web.handler.UserSessionHandler;
import io.vertx.ext.web.sstore.LocalSessionStore;
import java.util.stream.Stream;
import io.vertx.ext.web.handler.sockjs.BridgeEventType;
import io.vertx.ext.web.handler.sockjs.BridgeOptions;
import io.vertx.ext.web.handler.sockjs.PermittedOptions;
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
import io.vertx.ext.web.RoutingContext;

/**
 * @author joci
 */
public class HTTPVerticle extends AbstractVerticle {
    private final Logger log = LoggerFactory.getLogger(HTTPVerticle.class);
    private Map<String, ServerWebSocket> sockets = new LinkedHashMap<String, ServerWebSocket>();

    private Map<String, JsonObject> placedBTDevices = new LinkedHashMap<String, JsonObject>();

    private Map<String, JsonObject> newBTDevices = new LinkedHashMap<String, JsonObject>();

    private Map<String, Map<String, Double>> btData = new LinkedHashMap<String, Map<String, Double>>();

    private Map<String, JsonObject> positionedRobots = new LinkedHashMap<String, JsonObject>();

    private List<String> activeRobots = new ArrayList<String>();

    private String webRoot;

    private static final double EPSILON = 0.000001;

    private boolean calculateThreeCircleIntersection(double x0, double y0, double r0, double x1, double y1,
            double r1, double x2, double y2, double r2, String robotid) {
        double a, dx, dy, d, h, rx, ry;
        double point2_x, point2_y;

        /* dx and dy are the vertical and horizontal distances between
        * the circle centers.
        */
        dx = x1 - x0;
        dy = y1 - y0;

        /* Determine the straight-line distance between the centers. */
        d = Math.sqrt((dy * dy) + (dx * dx));

        /* Check for solvability. */
        if (d > (r0 + r1)) {
            log.info("no solution. circles do not intersect.");
            /* no solution. circles do not intersect. */
            return false;
        }
        if (d < Math.abs(r0 - r1)) {
            log.info("no solution. one circle is contained in the other");
            /* no solution. one circle is contained in the other */
            return false;
        }

        /* 'point 2' is the point where the line through the circle
        * intersection points crosses the line between the circle
        * centers.
        */

        /* Determine the distance from point 0 to point 2. */
        a = ((r0 * r0) - (r1 * r1) + (d * d)) / (2.0 * d);

        /* Determine the coordinates of point 2. */
        point2_x = x0 + (dx * a / d);
        point2_y = y0 + (dy * a / d);

        /* Determine the distance from point 2 to either of the
        * intersection points.
        */
        h = Math.sqrt((r0 * r0) - (a * a));

        /* Now determine the offsets of the intersection points from
        * point 2.
        */
        rx = -dy * (h / d);
        ry = dx * (h / d);

        /* Determine the absolute intersection points. */
        double intersectionPoint1_x = point2_x + rx;
        double intersectionPoint2_x = point2_x - rx;
        double intersectionPoint1_y = point2_y + ry;
        double intersectionPoint2_y = point2_y - ry;

        double finalPosX = (intersectionPoint1_x + intersectionPoint2_x) / 2;
        double finalPosY = (intersectionPoint1_y + intersectionPoint2_y) / 2;

        log.info("INTERSECTION Circle1 AND Circle2:" + "(" + intersectionPoint1_x + "," + intersectionPoint1_y + ")"
                + " AND (" + intersectionPoint2_x + "," + intersectionPoint2_y + ")");

        /* Lets determine if circle 3 intersects at either of the above intersection points. */
        dx = intersectionPoint1_x - x2;
        dy = intersectionPoint1_y - y2;
        double d1 = Math.sqrt((dy * dy) + (dx * dx));

        dx = intersectionPoint2_x - x2;
        dy = intersectionPoint2_y - y2;
        double d2 = Math.sqrt((dy * dy) + (dx * dx));

        if (Math.abs(d1 - r2) < EPSILON) {
            log.info("INTERSECTION Circle1 AND Circle2 AND Circle3:" + "(" + intersectionPoint1_x + ","
                    + intersectionPoint1_y + ")");
            finalPosX = intersectionPoint1_x;
            finalPosY = intersectionPoint1_y;
        } else if (Math.abs(d2 - r2) < EPSILON) {
            log.info("INTERSECTION Circle1 AND Circle2 AND Circle3:" + "(" + intersectionPoint2_x + ","
                    + intersectionPoint2_y + ")"); //here was an error
        } else {
            log.info("INTERSECTION Circle1 AND Circle2 AND Circle3:" + "NONE");
        }

        positionedRobots.remove(robotid);
        JsonObject response = new JsonObject();
        response.put("robotId", robotid);
        response.put("x", finalPosX);
        response.put("y", finalPosY);
        positionedRobots.put(robotid, response);
        vertx.eventBus().publish("robot.position", Json.encode(response));
        return true;
    }

    // Create some static test entity user
    private static Map<Integer, User> users = new LinkedHashMap<>();

    {
        User adalee = new User("Adalee Smith", "D 0.122");
        users.put(adalee.getId(), adalee);

        User bob = new User("Bob Anderson", "D 0.120");
        users.put(bob.getId(), bob);
    }

    private SockJSHandler eventBusHandler() {
        BridgeOptions options = new BridgeOptions()
                .addOutboundPermitted(new PermittedOptions().setAddressRegex("new.robot"));
        options.addOutboundPermitted(new PermittedOptions().setAddressRegex("logout.robot"));
        options.addOutboundPermitted(new PermittedOptions().setAddressRegex("new.bt.device"));
        options.addOutboundPermitted(new PermittedOptions().setAddressRegex("placed.bt.device"));
        options.addOutboundPermitted(new PermittedOptions().setAddressRegex("robot.position"));
        options.addInboundPermitted(new PermittedOptions().setAddressRegex("login.robot"));
        options.addInboundPermitted(new PermittedOptions().setAddressRegex("login.client"));
        options.addInboundPermitted(new PermittedOptions().setAddressRegex("robot.\\.[0-9]+"));
        options.addInboundPermitted(new PermittedOptions().setAddressRegex("place.bt.device"));
        // use this to send message to the clients:
        // routingContext.vertx().eventBus().publish("muhaha", routingContext.getBodyAsString());
        return SockJSHandler.create(vertx).bridge(options, event -> {
            if (event.type() == BridgeEventType.SOCKET_CREATED) {
                log.info("A socket was created");
            }
            if (event.type() == BridgeEventType.SOCKET_CLOSED) {
                log.info("A socket was closed");
                if (activeRobots.contains(event.socket().writeHandlerID())) {
                    log.info("Robot logged out");
                    activeRobots.remove(event.socket().writeHandlerID());
                    JsonObject response = new JsonObject();
                    response.put("robotId", event.socket().writeHandlerID());
                    vertx.eventBus().publish("logout.robot", Json.encode(response));
                }
            }
            if (event.type() == BridgeEventType.REGISTER) {
                log.info("A handler was registered");
                log.info(event.rawMessage());
            }
            if (event.type() == BridgeEventType.SEND) {
                log.info("Client sent a message: " + event.rawMessage());
                if (event.rawMessage().getString("address").equals("login.robot")
                        && !activeRobots.contains(event.socket().writeHandlerID())) {
                    log.info("Robot logged in");
                    activeRobots.add(event.socket().writeHandlerID());
                    JsonObject response = new JsonObject();
                    response.put("robotId", event.socket().writeHandlerID());
                    vertx.eventBus().publish("new.robot", Json.encode(response));
                } else if (event.rawMessage().getString("address").equals("login.robot")) {
                    log.info("Robot already logged in");
                }

                if (event.rawMessage().getString("address").equals("login.client")) {
                    for (String key : sockets.keySet()) {
                        log.info("Send active robots");
                        JsonObject response = new JsonObject();
                        response.put("robotId", key);
                        vertx.eventBus().publish("new.robot", Json.encode(response));
                    }

                    for (JsonObject value : placedBTDevices.values()) {
                        vertx.eventBus().publish("placed.bt.device", Json.encode(value));
                    }

                    for (JsonObject value : newBTDevices.values()) {
                        vertx.eventBus().publish("new.bt.device", Json.encode(value));
                    }

                    for (JsonObject value : positionedRobots.values()) {
                        vertx.eventBus().publish("robot.position", Json.encode(value));
                    }
                }

                if (event.rawMessage().getString("address").equals("place.bt.device")) {

                    JsonObject data = event.rawMessage().getJsonObject("body");
                    log.info("Place bt device: " + data.getString("address"));
                    placedBTDevices.remove(data.getString("address"));
                    placedBTDevices.put(data.getString("address"), data);
                    newBTDevices.remove(data.getString("address"));
                    vertx.eventBus().publish("placed.bt.device", Json.encode(data));
                }
            }
            event.complete(true);
        });
    }

    private void getAllUser(RoutingContext routingContext) {
        log.info("Get all users");
        routingContext.response().putHeader("content-type", "application/json; charset=utf-8")
                .end(Json.encodePrettily(users.values()));
    }

    /**
     * Preload data
     *
     * @param routingContext
     */
    private void getInfo(RoutingContext routingContext) {
        JsonObject resp = new JsonObject();
        resp.put("preload", "data");

        routingContext.response().putHeader("content-type", "application/json; charset=utf-8")
                .end(Json.encodePrettily(resp));
    }

    @Override
    public void start() {

        init();
        HttpServer http = vertx.createHttpServer();
        Router router = Router.router(vertx);

        // Setup websocket connection handling
        router.route("/eventbus/*").handler(eventBusHandler());

        router.route("/ws").handler(this::handleWebSocketConnection);

        // Handle robot position data
        router.route("/api/robotposition/:data").handler(this::handleRobotPositionData);

        // Setup http session auth handling
        router.route().handler(CookieHandler.create());
        router.route().handler(BodyHandler.create());
        router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));

        // Simple auth service which uses a properties file for user/role info
        AuthProvider authProvider = ShiroAuth.create(vertx, ShiroAuthRealmType.PROPERTIES, new JsonObject());

        // We need a user session handler too to make sure the user is stored in
        // the session between requests
        router.route().handler(UserSessionHandler.create(authProvider));

        // Any requests to URI starting '/rest/' require login
        router.route("/rest/*").handler(SimpleAuthHandler.create(authProvider));

        // Serve the static private pages from directory 'rest'
        // user/getAll TEST page
        router.route("/rest/user/getAll").handler(this::getAllUser);

        // Preload
        router.route("/rest/info").handler(this::getInfo);

        router.route("/loginhandler").handler(SimpleLoginHandler.create(authProvider));

        router.route("/logout").handler(context -> {
            context.clearUser();
            // Status OK
            context.response().setStatusCode(HttpStatus.SC_OK).end();
        });

        router.route().handler(StaticHandler.create().setWebRoot(webRoot));
        http.websocketHandler(ws -> {
            String id = java.util.UUID.randomUUID().toString();
            ws.handler(buffer -> {
                try {
                    JsonObject response = new JsonObject(buffer.toString());
                    if (response.getString("action") != null && response.getString("action").equals("login.robot")
                            && sockets.get(id) == null) {
                        log.info("robot logged in:" + id);
                        sockets.put(id, ws);
                        JsonObject pb = new JsonObject();
                        pb.put("robotId", id);
                        vertx.eventBus().publish("new.robot", Json.encode(pb));
                    } else if (response.getString("action") != null
                            && response.getString("action").equals("found.bluetooth")) {
                        log.info("found.bluetooth");
                        JsonObject pb = response.getJsonObject("data");
                        if (placedBTDevices.get(pb.getString("address")) == null
                                && newBTDevices.get(pb.getString("address")) == null) {
                            JsonObject data = new JsonObject(Json.encode(pb));
                            data.remove("rssi");
                            newBTDevices.put(pb.getString("address"), data);
                            log.info("New bt device: " + buffer);
                            vertx.eventBus().publish("new.bt.device", Json.encode(data));
                        }

                        btData.get(id).remove(pb.getString("address"));

                        log.info("Update bt data: " + id + " " + pb.getString("address") + " "
                                + pb.getInteger("rssi"));
                        double x = (pb.getInteger("rssi") - (-40.0)) / ((-10.0) * 2.0);
                        log.info("sub calc res: " + x);
                        double d = Math.pow(10.0, x);
                        //RSSI (dBm) = -10n log10(d) + A
                        log.info("the calculated distance is around: " + d + "m");
                        btData.get(id).put(pb.getString("address"), d * 27);
                    } else if (response.getString("action") != null
                            && response.getString("action").equals("start.bluetooth.scan")) {
                        log.info("start.bluetooth.scan");
                        btData.remove(id);
                        btData.put(id, new LinkedHashMap<String, Double>());

                    } else if (response.getString("action") != null
                            && response.getString("action").equals("finished.bluetooth.scan")) {
                        log.info("finished.bluetooth.scan");
                        if (btData.get(id).size() >= 3) {
                            double x0 = 0, y0 = 0, r0 = 0, x1 = 0, y1 = 0, r1 = 0, x2 = 0, y2 = 0, r2 = 0;
                            int i = 0;
                            for (Map.Entry<String, Double> entry : btData.get(id).entrySet()) {
                                String key = entry.getKey();
                                JsonObject placedBT = placedBTDevices.get(key);
                                if (placedBT == null) {
                                    log.info("placedBT is null, the key was: " + key);
                                    continue;
                                }
                                double value = entry.getValue();
                                if (i == 0) {

                                    x0 = placedBT.getDouble("x");
                                    y0 = placedBT.getDouble("y");
                                    r0 = value;
                                    log.info("fill first circle x: " + x0 + " y: " + y0 + " r: " + r0);
                                } else if (i == 1) {

                                    x1 = placedBT.getDouble("x");
                                    y1 = placedBT.getDouble("y");
                                    r1 = value;
                                    log.info("fill second circle x: " + x1 + " y: " + y1 + " r: " + r1);
                                } else if (i == 2) {

                                    x2 = placedBT.getDouble("x");
                                    y2 = placedBT.getDouble("y");
                                    r2 = value;
                                    log.info("fill third circle x: " + x2 + " y: " + y2 + " r: " + r2);
                                } else {
                                    break;
                                }
                                i++;
                            }

                            if (i == 3) {
                                log.info("start calculation");
                                if (calculateThreeCircleIntersection(x0, y0, r0, x1, y1, r1, x2, y2, r2, id)) {
                                    log.info("solved circle intersection");
                                } else {
                                    log.info("cannot solve circle interseciton");
                                }
                            } else {
                                log.info("there was not enough BT device: " + i);
                            }

                        } else {
                            log.info("There is not enough BT data to calculate the location of the robot.");
                        }
                    }

                    log.info("got the following message from " + id + ": " + Json.encode(response));

                } catch (Exception e) {
                    log.info("Cannot process the following buffer: " + buffer);
                    log.error(e);
                }
            });
            ws.endHandler(e -> {
                JsonObject response = new JsonObject();
                response.put("robotId", id);
                vertx.eventBus().publish("logout.robot", Json.encode(response));
                sockets.remove(id);
                positionedRobots.remove(id);
                btData.remove(id);
                log.info("The following robot logged out: " + id);
            });
        }).requestHandler(router::accept).listen(Integer.getInteger("http.port"),
                System.getProperty("http.address", "0.0.0.0"));
    }

    private void answer(String id) {
        sockets.get(id).writeFinalTextFrame("Geronimo!");
    }

    private void handleWebSocketConnection(RoutingContext context) {
    }

    private void handleRobotPositionData(RoutingContext context) {
        HttpServerRequest req = context.request();
        String data = req.getParam("data");
        System.out.println("Data from robot:");
        Stream.of(data.split("_")).forEach(System.out::println);
        System.out.println("\n");
        context.response().end();
    }

    private void init() {
        log.info("FrontendVerticle starting");
        //ockets = new ArrayList<>();
        webRoot = config().getString("webRoot", "src/main/resources/webroot");
    }
}