diff --git a/server/src/main/java/DebugClInterface.kt b/server/src/main/java/DebugClInterface.kt index 32a5a058a63..e824c4ed612 100644 --- a/server/src/main/java/DebugClInterface.kt +++ b/server/src/main/java/DebugClInterface.kt @@ -1,32 +1,28 @@ -import Exceptions.InactiveCarException +import RoomScanner.serialize import algorithm.AbstractAlgorithm import algorithm.AlgorithmThread import algorithm.RoomBypassingAlgorithm import algorithm.RoomModel -import car.client.CarClient -import car.client.Client -import io.netty.buffer.Unpooled -import io.netty.handler.codec.http.* +import net.car.client.Client import objects.Car import objects.Environment +import java.net.ConnectException import java.rmi.UnexpectedException -import java.util.concurrent.Exchanger object DebugClInterface { - val exchanger = Exchanger() + //todo remove var algorithmImpl: AbstractAlgorithm? = null var algorithmThread: AlgorithmThread? = null private val routeRegex = Regex("route [0-9]{1,10}") private val sonarRegex = Regex("sonar [0-9]{1,10}") - private val helpString = "available commands:\n" + + private val helpString = "available handlers:\n" + "cars - get list of connected cars\n" + - "route [car_id] - setting a route for car with car id.\n" + - "refloc - refresh all car locations\n" + + "route [car_id] - setting a route for net.car with net.car id.\n" + "sonar [car_id] - get sonar data\n" + - "dbinfo [car_id] [type] - refresh all car locations\n" + - "lines - print lines, detected by car\n" + + "dbinfo [car_id] [type] - refresh all net.car locations\n" + + "lines - print lines, detected by net.car\n" + "alg [count] - run algorithm. Make [count] iteration\n" + "type is string name of value or int value. available values: MEMORYSTATS - 0\n" + "stop - exit from this interface and stop all threads\n" @@ -66,7 +62,6 @@ object DebugClInterface { }) } "route" -> executeRouteCommand(readString) - "refloc" -> executeRefreshLocationCommand() "sonar" -> executeSonarCommand(readString) "dbinfo" -> executeDebugInfoCommand(readString) "lines" -> algorithm.RoomModel.walls.forEach { println(it.line) } @@ -74,7 +69,7 @@ object DebugClInterface { val tmp = algorithmImpl if (tmp is RoomBypassingAlgorithm) { println("walls: ${RoomModel.walls}") - println("x: ${tmp.carX} y: ${tmp.carY} angle:${tmp.carAngle}") + println("x: ${tmp.thisCar.x} y: ${tmp.thisCar.y} angle:${tmp.thisCar.angle}") } } "alg" -> executeAlg(readString) @@ -90,10 +85,9 @@ object DebugClInterface { val window = params[3].toInt() val request = SonarExploreAngleRequest.BuilderSonarExploreAngleRequest(angle, window).build() - val responseData = CarClient.sendRequest( - car, - CarClient.Request.EXPLORE_ANGLE, - CarClient.serialize(request.getSizeNoTag(), { request.writeTo(it) }) + val responseData = car.carConnection.sendRequest( + Client.Request.EXPLORE_ANGLE, + serialize(request.getSizeNoTag(), { request.writeTo(it) }) ).get().responseBodyAsBytes val distances = SonarExploreAngleResponse.BuilderSonarExploreAngleResponse(IntArray(0)).parseFrom(CodedInputStream(responseData)).build().distances @@ -112,7 +106,7 @@ object DebugClInterface { var alg = algorithmImpl if (alg == null) { - alg = RoomBypassingAlgorithm(Environment.map.values.last(), exchanger) + alg = RoomBypassingAlgorithm(Environment.map.values.last()) } var algThread = algorithmThread if (algThread == null) { @@ -131,15 +125,14 @@ object DebugClInterface { val request = DebugRequest.BuilderDebugRequest(type).build() val requestType = when (type) { - DebugRequest.Type.MEMORY_STATS -> CarClient.Request.DEBUG_MEMORY - DebugRequest.Type.SONAR_STATS -> CarClient.Request.DEBUG_SONAR + DebugRequest.Type.MEMORY_STATS -> Client.Request.DEBUG_MEMORY + DebugRequest.Type.SONAR_STATS -> Client.Request.DEBUG_SONAR else -> throw UnexpectedException(type.toString()) } - val responseData = CarClient.sendRequest( - car, + val responseData = car.carConnection.sendRequest( requestType, - CarClient.serialize(request.getSizeNoTag(), { request.writeTo(it) }) + serialize(request.getSizeNoTag(), { request.writeTo(it) }) ).get().responseBodyAsBytes when (type) { @@ -180,16 +173,15 @@ object DebugClInterface { Environment.map[id] }) if (car == null) { - println("car with id=$id not found") + println("net.car with id=$id not found") return } val requestMessage = getSonarRequest() ?: return val requestBytes = ByteArray(requestMessage.getSizeNoTag()) requestMessage.writeTo(CodedOutputStream(requestBytes)) - val request = getDefaultHttpRequest(car.host, sonarUrl, requestBytes) try { - Client.sendRequest(request, car.host, car.port, mapOf(Pair("angles", requestMessage.angles))) - } catch (e: InactiveCarException) { + car.carConnection.sendRequest(Client.Request.SONAR, requestBytes) + } catch (e: ConnectException) { synchronized(Environment, { Environment.map.remove(id) }) @@ -223,29 +215,6 @@ object DebugClInterface { } } - private fun getRequestOptionId(id: Int): Map { - return mapOf(Pair("uid", id)) - } - - private fun executeRefreshLocationCommand() { - val cars = synchronized(Environment, { Environment.map.values }) - val inactiveCars = mutableListOf() - for (car in cars) { - val request = getDefaultHttpRequest(car.host, getLocationUrl, ByteArray(0)) - try { - Client.sendRequest(request, car.host, car.port, getRequestOptionId(car.uid)) - } catch (e: InactiveCarException) { - inactiveCars.add(car.uid) - } - println("ref loc done") - } - synchronized(Environment, { - for (id in inactiveCars) { - Environment.map.remove(id) - } - }) - } - private fun executeRouteCommand(readString: String) { if (!routeRegex.matches(readString)) { println("incorrect args of command route.") @@ -266,16 +235,15 @@ object DebugClInterface { Environment.map[id] }) if (car == null) { - println("car with id=$id not found") + println("net.car with id=$id not found") return } val routeMessage = getRouteMessage() ?: return val requestBytes = ByteArray(routeMessage.getSizeNoTag()) routeMessage.writeTo(CodedOutputStream(requestBytes)) - val request = getDefaultHttpRequest(car.host, setRouteUrl, requestBytes) try { - Client.sendRequest(request, car.host, car.port, mapOf(Pair("uid", id))) - } catch (e: InactiveCarException) { + car.carConnection.sendRequest(Client.Request.ROUTE_METRIC, requestBytes) + } catch (e: ConnectException) { synchronized(Environment, { Environment.map.remove(id) }) @@ -283,10 +251,9 @@ object DebugClInterface { } private fun getRouteMessage(): RouteRequest? { - println("print way points in polar coordinate als [distance] [rotation target] in metres and degrees." + - "after enter all points print \"done\". for reset route print \"reset\"") - println("e.g. for move from (x,y) to (x+1,y) and back to (x,y) you need enter:" + - "1 0[enter] 1 180[enter] done") + println("print way points in polar coordinate als [distance/degrees] [direction] in sm and degrees." + + "after enter all points print \"done\". for reset route print \"reset\". available directions:" + + "0 - FORWARD, 1 - BACKWARD, 2 - LEFT, 3 - RIGHT") val distances = arrayListOf() val angles = arrayListOf() while (true) { @@ -315,13 +282,4 @@ object DebugClInterface { } } } - - private fun getDefaultHttpRequest(host: String, url: String, bytes: ByteArray): DefaultFullHttpRequest { - val request = DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, url, Unpooled.copiedBuffer(bytes)) - request.headers().set(HttpHeaderNames.HOST, host) - request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE) - request.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, request.content().readableBytes()) - return request - } - -} \ No newline at end of file +} diff --git a/server/src/main/java/Exceptions/InactiveCarException.kt b/server/src/main/java/Exceptions/InactiveCarException.kt deleted file mode 100644 index 2cb8af5042c..00000000000 --- a/server/src/main/java/Exceptions/InactiveCarException.kt +++ /dev/null @@ -1,5 +0,0 @@ -package Exceptions - - -class InactiveCarException : Exception() { -} \ No newline at end of file diff --git a/server/src/main/java/Exceptions/SonarDataException.kt b/server/src/main/java/Exceptions/SonarDataException.kt deleted file mode 100644 index 536e13a789c..00000000000 --- a/server/src/main/java/Exceptions/SonarDataException.kt +++ /dev/null @@ -1,4 +0,0 @@ -package Exceptions - -class SonarDataException() : Exception() { -} \ No newline at end of file diff --git a/server/src/main/java/RoomScanner/CarController.kt b/server/src/main/java/RoomScanner/CarController.kt index 8fa99e2a856..333fe2148fb 100644 --- a/server/src/main/java/RoomScanner/CarController.kt +++ b/server/src/main/java/RoomScanner/CarController.kt @@ -4,7 +4,7 @@ import CodedInputStream import RouteMetricRequest import SonarRequest import SonarResponse -import car.client.CarClient +import net.car.client.Client import objects.Car class CarController(var car: Car) { @@ -15,8 +15,10 @@ class CarController(var car: Car) { RIGHT(3); } + //todo use x,y,angle from car object. x,y in sm, angle in degrees var position = Pair(0.0, 0.0) private set + private var angle = 0.0 private val CHARGE_CORRECTION = 0.97 private val MAX_ANGLE = 360.0 @@ -24,8 +26,6 @@ class CarController(var car: Car) { private val MIN_ROTATION = 10 private val MAX_VALID_DISTANCE = 100.0 - private var angle = 0.0 - fun moveTo(to: Pair, distance: Double) { val driveAngle = (Math.toDegrees(Math.atan2(to.second, to.first)) + MAX_ANGLE) % MAX_ANGLE rotateOn(driveAngle) @@ -70,8 +70,10 @@ class CarController(var car: Car) { fun scan(angles: IntArray): List { val request = SonarRequest.BuilderSonarRequest(angles, IntArray(angles.size, { 1 }), 1, SonarRequest.Smoothing.NONE).build() - val data = CarClient.serialize(request.getSizeNoTag(), { i -> request.writeTo(i) }) - val response = CarClient.sendRequest(car, CarClient.Request.SONAR, data).get().responseBodyAsBytes + + val data = serialize(request.getSizeNoTag(), { request.writeTo(it) }) + + val response = car.carConnection.sendRequest(Client.Request.SONAR, data).get().responseBodyAsBytes val result = SonarResponse.BuilderSonarResponse(IntArray(0)).parseFrom(CodedInputStream(response)).build().distances.map { it.toDouble() } return result @@ -88,8 +90,7 @@ class CarController(var car: Car) { fun drive(direction: Direction, distance: Int) { val request = RouteMetricRequest.BuilderRouteMetricRequest(intArrayOf((distance * CHARGE_CORRECTION).toInt()), intArrayOf(direction.id)).build() - val data = CarClient.serialize(request.getSizeNoTag(), { i -> request.writeTo(i) }) - CarClient.sendRequest(car, CarClient.Request.ROUTE_METRIC, data).get() + val data = serialize(request.getSizeNoTag(), { i -> request.writeTo(i) }) + car.carConnection.sendRequest(Client.Request.ROUTE_METRIC, data).get() } - } diff --git a/server/src/main/java/RoomScanner/RoomScanner.kt b/server/src/main/java/RoomScanner/RoomScanner.kt index df41bb953a2..22420b76ae9 100644 --- a/server/src/main/java/RoomScanner/RoomScanner.kt +++ b/server/src/main/java/RoomScanner/RoomScanner.kt @@ -6,7 +6,7 @@ class RoomScanner(val controller: CarController) : Thread() { private val GHOST_LEVEL = 50 override fun run() { - println("Car connected ${controller.car.host}") + println("Car connected ${controller.car.carConnection.host}") for (i in 1..100) { step() println(plot(points)) diff --git a/server/src/main/java/RoomScanner/utils.kt b/server/src/main/java/RoomScanner/utils.kt index 8cc6d87c780..c33ba9abdc6 100644 --- a/server/src/main/java/RoomScanner/utils.kt +++ b/server/src/main/java/RoomScanner/utils.kt @@ -1,5 +1,7 @@ package RoomScanner +import CodedOutputStream + fun horizon(): IntArray { val horizon = IntArray(180 / 5, { it * 5 }) horizon.reverse() @@ -21,6 +23,13 @@ fun angleDistance(from: Double, to: Double): Double { return distance * direction } +inline fun serialize(size: Int, writeTo: (CodedOutputStream) -> Unit): ByteArray { + val bytes = ByteArray(size) + writeTo(CodedOutputStream(bytes)) + + return bytes +} + fun maxSuffix(first: List, second: List, distance: (T, T) -> Double): Int { val distances = first.mapIndexed { i: Int, t: T -> var score = 0.0 diff --git a/server/src/main/java/ServerMain.kt b/server/src/main/java/ServerMain.kt index fe819113d36..75813987b79 100644 --- a/server/src/main/java/ServerMain.kt +++ b/server/src/main/java/ServerMain.kt @@ -1,23 +1,15 @@ import RoomScanner.CarController import RoomScanner.RoomScanner -import car.Dropper -import car.client.Client +import net.car.Dropper +import net.car.client.Client import objects.Environment -val carServerPort: Int = 7925 -val webServerPort: Int = 7926 -val getLocationUrl = "/getLocation" -val setRouteUrl = "/route" -val setRouteMetricUrl = "/routeMetric" -val connectUrl = "/connect" -val debugMemoryUrl = "/debug/memory" -val sonarUrl = "/sonar" fun main(args: Array) { var roomScanner: RoomScanner? = null - val carServer = car.server.Server.getCarServerThread(carServerPort) - val webServer = web.server.Server.getWebServerThread(webServerPort) - val carsDestroy = Dropper.getCarsDestroyThread() + val carServer = net.car.server.Server.createCarServerThread() + val webServer = net.web.server.Server.createWebServerThread() + val carsDestroy = Dropper.createCarsDestroyThread() carServer.start() carsDestroy.start() webServer.start() @@ -29,6 +21,9 @@ fun main(args: Array) { } } + + //todo запуск потока алгоритма нужно вынести сюда, + //todo а debug интерфейсутребуется исключительно возможность установки кол-ва итераций //CL user interface DebugClInterface.run() diff --git a/server/src/main/java/algorithm/AbstractAlgorithm.kt b/server/src/main/java/algorithm/AbstractAlgorithm.kt index 65c1236509b..1bad3d3ee5a 100644 --- a/server/src/main/java/algorithm/AbstractAlgorithm.kt +++ b/server/src/main/java/algorithm/AbstractAlgorithm.kt @@ -1,25 +1,21 @@ package algorithm +import CodedInputStream import CodedOutputStream -import Exceptions.InactiveCarException -import Exceptions.SonarDataException +import Logger import RouteMetricRequest import SonarRequest +import SonarResponse import algorithm.geometry.Angle import algorithm.geometry.AngleData -import io.netty.buffer.Unpooled -import io.netty.handler.codec.http.* +import net.car.client.Client import objects.Car -import setRouteMetricUrl -import sonarUrl -import sun.rmi.runtime.Log +import java.net.ConnectException import java.util.* -import java.util.concurrent.Exchanger import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException -import kotlin.concurrent.thread -abstract class AbstractAlgorithm(val thisCar: Car, val exchanger: Exchanger) { +abstract class AbstractAlgorithm(val thisCar: Car) { open val ATTEMPTS = 1 open val SMOOTHING = SonarRequest.Smoothing.NONE @@ -37,7 +33,6 @@ abstract class AbstractAlgorithm(val thisCar: Car, val exchanger: Exchanger() private val defaultAngles = arrayOf(Angle(0), Angle(70), Angle(75), Angle(80), Angle(85), Angle(90), Angle(95), Angle(100), Angle(105), Angle(110), Angle(180)) - // private val defaultAngles = arrayOf(Angle(0), Angle(60), Angle(70), Angle(80), Angle(90), Angle(100), Angle(110), Angle(120), Angle(180)) protected var requiredAngles = defaultAngles protected enum class CarState { @@ -60,22 +55,21 @@ abstract class AbstractAlgorithm(val thisCar: Car, val exchanger: Exchanger()) - } catch (e: InactiveCarException) { + thisCar.carConnection.sendRequest(Client.Request.ROUTE_METRIC, messageBytes).get(60, TimeUnit.SECONDS) + } catch (e: ConnectException) { println("connection error!") - } - try { - exchanger.exchange(IntArray(0), 60, TimeUnit.SECONDS) - return } catch (e: TimeoutException) { - println("don't have response from car. Timeout!") + println("don't have response from net.car. Timeout!") } return } fun iterate() { - Logger.log("============= STARTING ITERATION ${iterationCounter} ============") + Logger.log("============= STARTING ITERATION $iterationCounter ============") Logger.indent() iterationCounter++ if (RoomModel.finished) { @@ -118,16 +107,15 @@ abstract class AbstractAlgorithm(val thisCar: Car, val exchanger: Exchanger { - return prevSonarDistances - } private fun getAngles(): Array { return requiredAngles @@ -186,7 +173,7 @@ abstract class AbstractAlgorithm(val thisCar: Car, val exchanger: Exchanger FORWARD LEFT -> RIGHT RIGHT -> LEFT - else -> throw IllegalArgumentException("Unexpected direction = ${dir} found during command inversion") + else -> throw IllegalArgumentException("Unexpected direction = $dir found during command inversion") } } return res.build() @@ -204,7 +191,7 @@ abstract class AbstractAlgorithm(val thisCar: Car, val exchanger: Exchanger 0 && history.size > 0) { @@ -216,18 +203,9 @@ abstract class AbstractAlgorithm(val thisCar: Car, val exchanger: Exchanger): CarState - protected abstract fun getCommand(anglesDistances: Map, state: CarState): RouteMetricRequest + protected abstract fun getCarState(anglesDistances: Map): CarState? + protected abstract fun getCommand(anglesDistances: Map, state: CarState): RouteMetricRequest? protected abstract fun afterGetCommand(route: RouteMetricRequest) abstract fun isCompleted(): Boolean - - private fun getDefaultHttpRequest(host: String, url: String, bytes: ByteArray): DefaultFullHttpRequest { - val request = DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, url, Unpooled.copiedBuffer(bytes)) - request.headers().set(HttpHeaderNames.HOST, host) - request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE) - request.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, request.content().readableBytes()) - return request - } - } \ No newline at end of file diff --git a/server/src/main/java/algorithm/RoomBypassingAlgorithm.kt b/server/src/main/java/algorithm/RoomBypassingAlgorithm.kt index 3099c8fad8f..be1cc86b95f 100644 --- a/server/src/main/java/algorithm/RoomBypassingAlgorithm.kt +++ b/server/src/main/java/algorithm/RoomBypassingAlgorithm.kt @@ -1,21 +1,24 @@ package algorithm -import Exceptions.SonarDataException +import Logger import Logger.log import RouteMetricRequest import SonarRequest import algorithm.geometry.* -import algorithm.geometry.Vector import objects.Car -import java.util.concurrent.Exchanger -class RoomBypassingAlgorithm(thisCar: Car, exchanger: Exchanger) : AbstractAlgorithm(thisCar, exchanger) { +class RoomBypassingAlgorithm(thisCar: Car) : AbstractAlgorithm(thisCar) { + + + //todo refactoring all algorithm package + + override val ATTEMPTS: Int = 5 override val SMOOTHING = SonarRequest.Smoothing.MEDIAN override val WINDOW_SIZE = 3 //SHOULD BE CALIBRATED BEFORE RUNNING!!!!!!!!!!! - private val CHARGE_CORRECTION = 0.87//on full charge ok is 0.83 - 0.86 + private val CHARGE_CORRECTION = 1.0//on full charge ok is 0.83 - 0.86 private val MAX_DISTANCE_TO_WALL_AHEAD = 55 // reached the corner and should turn left private val OUTER_CORNER_DISTANCE_THRESHOLD = 90 // reached outer corner @@ -31,11 +34,7 @@ class RoomBypassingAlgorithm(thisCar: Car, exchanger: Exchanger) : Abs private var isCompleted = false private var circleFound = false - var carX = 0 - var carY = 0 - var carAngle = 0 - - override fun getCarState(anglesDistances: Map): CarState { + override fun getCarState(anglesDistances: Map): CarState? { return CarState.WALL } @@ -45,9 +44,9 @@ class RoomBypassingAlgorithm(thisCar: Car, exchanger: Exchanger) : Abs val wallAngle = calculateAngle(anglesDistances, state) Logger.indent() resultBuilder.setDistances(getIntArray(20, wallAngle.degs(), 75)) + thisCar.angle = (RoomModel.walls.last().wallAngleOX.degs() - wallAngle.degs()).toDouble() addWall(-wallAngle) Logger.outdent() - carAngle = RoomModel.walls.last().wallAngleOX.degs() calibrateAfterRotate = true return resultBuilder.build() } @@ -57,8 +56,8 @@ class RoomBypassingAlgorithm(thisCar: Car, exchanger: Exchanger) : Abs resultBuilder.setDirections(getIntArray(LEFT, FORWARD)) val wallAngle = calculateAngle(anglesDistances, state) resultBuilder.setDistances(getIntArray(wallAngle.degs(), 15)) + thisCar.angle = (RoomModel.walls.last().wallAngleOX.degs() + wallAngle.degs()).toDouble() addWall(wallAngle) - carAngle = RoomModel.walls.last().wallAngleOX.degs() calibrateAfterRotate = true return resultBuilder.build() } @@ -117,7 +116,7 @@ class RoomBypassingAlgorithm(thisCar: Car, exchanger: Exchanger) : Abs private fun addWall(angleWithPrevWall: Angle) { log("Adding wall") Logger.indent() - RoomModel.updateWalls() + updateWalls() Logger.outdent() val firstWall = RoomModel.walls.first() val lastWall = RoomModel.walls.last() @@ -135,14 +134,40 @@ class RoomBypassingAlgorithm(thisCar: Car, exchanger: Exchanger) : Abs } } - override fun getCommand(anglesDistances: Map, state: CarState): RouteMetricRequest { + fun updateWalls() { + synchronized(RoomModel) { + val walls = RoomModel.walls + if (walls.size < 2) { + // no walls to intersect + return + } + val line1 = walls.last().line + val line2 = walls[RoomModel.walls.size - 2].line + + val intersection: Point = line1.intersect(line2) + + + val lastWall = walls[RoomModel.walls.size - 1] + lastWall.pushFrontPoint(intersection) + if (lastWall.isHorizontal()) { + lastWall.line = Line(0.0, 1.0, -intersection.y) + } else if (lastWall.isVertical()) { + lastWall.line = Line(1.0, 0.0, -intersection.x) + } + + walls[walls.size - 2].pushBackPoint(intersection) + walls[walls.size - 2].markAsFinished() + } + } + + override fun getCommand(anglesDistances: Map, state: CarState): RouteMetricRequest? { val dist0 = anglesDistances[Angle(0)]!! val dist70 = anglesDistances[Angle(70)]!! val dist80 = anglesDistances[Angle(80)]!! val dist90 = anglesDistances[Angle(90)]!! val dist100 = anglesDistances[Angle(100)]!! val dist110 = anglesDistances[Angle(110)]!! - val sonarAngle = carAngle - 90 + val sonarAngle = (thisCar.angle - 90).toInt() if (anglesDistances.filter { it.value.angle.degs() >= 60 @@ -152,7 +177,7 @@ class RoomBypassingAlgorithm(thisCar: Car, exchanger: Exchanger) : Abs log("Found to many -1 in angle distances, falling back") rollback() //todo Теоретически такая ситуация может быть валидной, если сразу после внутреннего угла идёт внешний - throw SonarDataException() + return null } val average = (anglesDistances.values @@ -187,8 +212,8 @@ class RoomBypassingAlgorithm(thisCar: Car, exchanger: Exchanger) : Abs // Add point to room map val point = Point( - x = carX + dist90.distance * Math.cos(degreesToRadian(sonarAngle)), - y = carY + dist90.distance * Math.sin(degreesToRadian(sonarAngle)) + x = thisCar.x + dist90.distance * Math.cos(degreesToRadian(sonarAngle)), + y = thisCar.y + dist90.distance * Math.sin(degreesToRadian(sonarAngle)) ) log("Adding middle point ${point.toString()} to wall ${RoomModel.walls.last().id}") RoomModel.walls.last().pushBackPoint(point) @@ -214,8 +239,8 @@ class RoomBypassingAlgorithm(thisCar: Car, exchanger: Exchanger) : Abs continue } val point = Point( - x = carX + curDist * Math.cos(degreesToRadian(carAngle - i)), - y = carY + curDist * Math.sin(degreesToRadian(carAngle - i)) + x = thisCar.x + curDist * Math.cos(degreesToRadian((thisCar.angle - i).toInt())), + y = thisCar.y + curDist * Math.sin(degreesToRadian((thisCar.angle - i).toInt())) ) log("Adding ${point.toString()} to wall ${RoomModel.walls.last().id}") RoomModel.walls.last().pushBackPoint(point) @@ -281,12 +306,12 @@ class RoomBypassingAlgorithm(thisCar: Car, exchanger: Exchanger) : Abs route.directions.forEachIndexed { idx, direction -> when (direction) { FORWARD -> { - carX += (Math.cos(degreesToRadian(carAngle.toInt())) * route.distances[idx]).toInt() - carY += (Math.sin(degreesToRadian(carAngle.toInt())) * route.distances[idx]).toInt() + thisCar.x += (Math.cos(degreesToRadian(thisCar.angle.toInt())) * route.distances[idx]).toInt() + thisCar.y += (Math.sin(degreesToRadian(thisCar.angle.toInt())) * route.distances[idx]).toInt() } BACKWARD -> { - carX -= (Math.cos(degreesToRadian(carAngle.toInt())) * route.distances[idx]).toInt() - carY -= (Math.sin(degreesToRadian(carAngle.toInt())) * route.distances[idx]).toInt() + thisCar.x -= (Math.cos(degreesToRadian(thisCar.angle.toInt())) * route.distances[idx]).toInt() + thisCar.y -= (Math.sin(degreesToRadian(thisCar.angle.toInt())) * route.distances[idx]).toInt() } LEFT -> { route.distances[idx] = (CHARGE_CORRECTION * route.distances[idx]).toInt() @@ -296,8 +321,8 @@ class RoomBypassingAlgorithm(thisCar: Car, exchanger: Exchanger) : Abs } } } - if (Math.round(Math.cos(Angle(carAngle).rads())).toInt() == 1 && carAngle != 0 - && Vector(carX.toDouble(), carY.toDouble()).length() < RANGE_FROM_ZERO_POINT_TO_FINISH_ALG) { + if (Math.round(Math.cos(Angle(thisCar.angle.toInt()).rads())).toInt() == 1 && thisCar.angle.toInt() != 0 + && Vector(thisCar.x.toDouble(), thisCar.y.toDouble()).length() < RANGE_FROM_ZERO_POINT_TO_FINISH_ALG) { log("Found circle!") circleFound = true } diff --git a/server/src/main/java/algorithm/RoomModel.kt b/server/src/main/java/algorithm/RoomModel.kt index d7fbbc185b0..d5a7a893ac0 100644 --- a/server/src/main/java/algorithm/RoomModel.kt +++ b/server/src/main/java/algorithm/RoomModel.kt @@ -1,114 +1,9 @@ package algorithm -import DebugClInterface -import DebugResponse -import Waypoints import algorithm.geometry.Angle -import algorithm.geometry.Line -import algorithm.geometry.Point import algorithm.geometry.Wall object RoomModel { val walls = arrayListOf(Wall(Angle(0))) var finished = false - - val linesModel = listOf(Line(0.0, 1.0, -300.0), - Line(1.0, 0.0, 150.0), - Line(0.0, 1.0, 20.0), - Line(1.0, 0.0, -200.0)) - - - // DEBUG BELOW - val rng = java.util.Random(42) - var iter = 0 - var currentPosition_x = 0 - var currentPosition_y = 0 - fun getUpdate(): Waypoints { - - val points = getWallsPoints() - - val begin_x = IntArray(points.size) - val begin_y = IntArray(points.size) - val end_x = IntArray(points.size) - val end_y = IntArray(points.size) - - - for (i in 0..points.size - 2) { - val curPoint = points[i] - val nextPoint = points[i + 1] - begin_x[i] = (curPoint.x + 0.5).toInt() - begin_y[i] = (curPoint.y + 0.5).toInt() - - end_x[i] = (nextPoint.x + 0.5).toInt() - end_y[i] = (nextPoint.y + 0.5).toInt() - } - return Waypoints.BuilderWaypoints(begin_x, begin_y, end_x, end_y, false).build() - } - - private fun getWallsPoints(): Array { - return walls.flatMap({ it.points }).toTypedArray() - } - - fun updateWalls() { - if (walls.size < 2) { - // no walls to intersect - return - } - val line1 = walls.last().line - val line2 = walls[walls.size - 2].line - - val intersection: Point = line1.intersect(line2) - - - val lastWall = walls[walls.size - 1] - lastWall.pushFrontPoint(intersection) - if (lastWall.isHorizontal()) { - lastWall.line = Line(0.0, 1.0, -intersection.y) - } else if (lastWall.isVertical()) { - lastWall.line = Line(1.0, 0.0, -intersection.x) - } - - walls[walls.size - 2].pushBackPoint(intersection) - walls[walls.size - 2].markAsFinished() - } - - fun getDebugInfo(): DebugResponse { - - val points = getWallsPoints() - - val begin_x = IntArray(points.size) - val begin_y = IntArray(points.size) - val end_x = IntArray(points.size) - val end_y = IntArray(points.size) - - val rawPoints = walls.flatMap({ it.rawPoints }).toTypedArray() - val pointsX = rawPoints.map { it.x.toInt() }.toIntArray() - val pointsY = rawPoints.map { it.y.toInt() }.toIntArray() - - for (i in 0..points.size - 2) { - val curPoint = points[i] - val nextPoint = points[i + 1] - begin_x[i] = (curPoint.x + 0.5).toInt() - begin_y[i] = (curPoint.y + 0.5).toInt() - - end_x[i] = (nextPoint.x + 0.5).toInt() - end_y[i] = (nextPoint.y + 0.5).toInt() - } - - val wallDistances = walls.filter { it.isFinished }.map { - val firstPoint = it.points.first() - val lastPoint = it.points.last() - val vector = algorithm.geometry.Vector(firstPoint, lastPoint) - vector.length().toInt() - }.toIntArray() - - val alg = DebugClInterface.algorithmImpl - if (alg is RoomBypassingAlgorithm) { - return DebugResponse.BuilderDebugResponse(begin_x, begin_y, end_x, end_y, alg.carX, - alg.carY, alg.carAngle, pointsX, pointsY, wallDistances).build() - } - return DebugResponse.BuilderDebugResponse(begin_x, begin_y, end_x, end_y, 0, - 0, 0, pointsX, pointsY, wallDistances).build() - - } } \ No newline at end of file diff --git a/server/src/main/java/car/Dropper.kt b/server/src/main/java/car/Dropper.kt deleted file mode 100644 index fa6bd19b2e8..00000000000 --- a/server/src/main/java/car/Dropper.kt +++ /dev/null @@ -1,54 +0,0 @@ -package car - -import Exceptions.InactiveCarException -import car.client.Client -import io.netty.handler.codec.http.DefaultHttpRequest -import io.netty.handler.codec.http.HttpMethod -import io.netty.handler.codec.http.HttpVersion -import objects.Environment -import kotlin.concurrent.thread - -object Dropper { - private val timeDeltaToDrop = 600000//time in ms. - // if car is inactive more than this time, this thread drop session with car - - fun getCarsDestroyThread(): Thread { - return thread(false, false, null, "dropCar", -1, getThreadCode()) - } - - private fun getThreadCode(): () -> Unit { - return { - var stopped = false - while (!stopped) { - synchronized(Environment, { - val currentTime = System.currentTimeMillis() - val keysToRemove = mutableListOf() - for ((key, value) in Environment.map) { - if ((value.lastAction + timeDeltaToDrop) < currentTime) { - keysToRemove.add(key) - } - } - dropInactiveCar(keysToRemove, Environment) - }) - try { - Thread.sleep(60000) - } catch (e: InterruptedException) { - println("thread for destroy cars stopped") - stopped = true - } - } - } - } - - private fun dropInactiveCar(keysToRemove: List, environment: Environment) { - for (key in keysToRemove) { - try { - val carValue = environment.map[key] ?: continue - val request = DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/ping") - Client.sendRequest(request, carValue.host, carValue.port, mapOf(Pair("uid", key))) - } catch (e: InactiveCarException) { - environment.map.remove(key) - } - } - } -} \ No newline at end of file diff --git a/server/src/main/java/car/client/CarClient.kt b/server/src/main/java/car/client/CarClient.kt deleted file mode 100644 index 46004285506..00000000000 --- a/server/src/main/java/car/client/CarClient.kt +++ /dev/null @@ -1,29 +0,0 @@ -package car.client - -import CodedOutputStream -import objects.Car -import org.asynchttpclient.ListenableFuture -import org.asynchttpclient.Response - -object CarClient { - enum class Request(val url: String) { - CONNECT("connect"), - GET_LOCATION("getLocation"), - ROUTE("route"), - ROUTE_METRIC("routeMetric"), - SONAR("sonar"), - DEBUG_MEMORY("debug/memory"), - DEBUG_SONAR("debug/sonar"), - EXPLORE_ANGLE("sonarExplore"); - } - - fun sendRequest(car: Car, request: Request, data: ByteArray): ListenableFuture - = Client.makeRequest("http://${car.host}:${car.port}/${request.url}", data) - - inline fun serialize(size: Int, writeTo: (CodedOutputStream) -> Unit): ByteArray { - val bytes = ByteArray(size) - writeTo(CodedOutputStream(bytes)) - - return bytes - } -} diff --git a/server/src/main/java/car/client/Client.kt b/server/src/main/java/car/client/Client.kt deleted file mode 100644 index 973f5a6f3ef..00000000000 --- a/server/src/main/java/car/client/Client.kt +++ /dev/null @@ -1,53 +0,0 @@ -package car.client - -import Exceptions.InactiveCarException -import io.netty.bootstrap.Bootstrap -import io.netty.channel.nio.NioEventLoopGroup -import io.netty.channel.socket.nio.NioSocketChannel -import io.netty.handler.codec.http.HttpRequest -import io.netty.util.AttributeKey -import org.asynchttpclient.DefaultAsyncHttpClient -import org.asynchttpclient.ListenableFuture -import org.asynchttpclient.Response -import java.net.ConnectException -import java.rmi.UnexpectedException - -object Client { - private val group = NioEventLoopGroup() - private val bootstrap = makeBootstrap() - private val client = DefaultAsyncHttpClient() - private val timeout = 5 * 60 * 60 * 1000 - - fun shutDownClient() { - client.close() - group.shutdownGracefully() - } - - fun sendRequest(request: HttpRequest, host: String, port: Int, options: Map) { - try { - bootstrap.attr(AttributeKey.valueOf("url"), request.uri()) - for ((key, value) in options) { - bootstrap.attr(AttributeKey.valueOf(key), value) - } - val ch = bootstrap.connect(host, port).sync().channel() - ch.writeAndFlush(request) -// ch.closeFuture().sync()//wait for answer - } catch (e: InterruptedException) { - - } catch (e: ConnectException) { - throw InactiveCarException() - } - } - - fun makeRequest(request: String, data: ByteArray): ListenableFuture = - client.preparePost(request).setBody(data).setRequestTimeout(timeout).execute() ?: throw UnexpectedException(request) - - private fun makeBootstrap(): Bootstrap { - val b = Bootstrap() - b.group(group).channel(NioSocketChannel().javaClass).handler(ClientInitializer()) - .attr(AttributeKey.newInstance("url"), "") - .attr(AttributeKey.newInstance("uid"), 0) - .attr(AttributeKey.newInstance("angles"), IntArray(0)) - return b - } -} \ No newline at end of file diff --git a/server/src/main/java/car/client/ClientHandler.kt b/server/src/main/java/car/client/ClientHandler.kt deleted file mode 100644 index 9144c28717f..00000000000 --- a/server/src/main/java/car/client/ClientHandler.kt +++ /dev/null @@ -1,111 +0,0 @@ -package car.client - -import CodedInputStream -import DebugClInterface -import DebugResponseMemoryStats -import LocationResponse -import SonarResponse -import debugMemoryUrl -import getLocationUrl -import io.netty.channel.ChannelHandlerContext -import io.netty.channel.SimpleChannelInboundHandler -import io.netty.handler.codec.http.DefaultHttpContent -import io.netty.util.AttributeKey -import objects.Environment -import setRouteMetricUrl -import setRouteUrl -import sonarUrl -import java.util.* -import java.util.concurrent.TimeUnit -import java.util.concurrent.TimeoutException - -class ClientHandler : SimpleChannelInboundHandler() { - - var contentBytes: ByteArray = ByteArray(0) - - override fun channelReadComplete(ctx: ChannelHandlerContext) { - val url = ctx.channel().attr(AttributeKey.valueOf("url")).get() - when (url) { - getLocationUrl -> { - val carUid = ctx.channel().attr(AttributeKey.valueOf("uid")).get() - val locData = LocationResponse.LocationData.BuilderLocationData(0, 0, 0).build() - val response = LocationResponse.BuilderLocationResponse(locData, 0).build() - response.mergeFrom(CodedInputStream(contentBytes)) - handlerGetLocationResponse(response, carUid) - } - debugMemoryUrl -> { - val response = DebugResponseMemoryStats.BuilderDebugResponseMemoryStats(0, 0, 0, 0).build() - response.mergeFrom(CodedInputStream(contentBytes)) - handlerDebugMemoryResponse(response) - } - sonarUrl -> { - val response = SonarResponse.BuilderSonarResponse(IntArray(0)).build() - response.mergeFrom(CodedInputStream(contentBytes)) - val angles = ctx.channel().attr(AttributeKey.valueOf("angles")).get() - handlerSonarResponse(response, angles) - } - setRouteUrl, setRouteMetricUrl -> { - try { - DebugClInterface.exchanger.exchange(IntArray(0), 20, TimeUnit.SECONDS) - } catch (e: InterruptedException) { - println("interrupted before sending datas to algorithm!") - } catch (e: TimeoutException) { - e.printStackTrace() - println("timeout") - } - } - else -> { - - } - } - ctx.close() - } - - private fun handlerGetLocationResponse(message: LocationResponse, carUid: Int) { - if (message.code == 0) { - val data = message.locationResponseData - synchronized(Environment, { - val car = Environment.map[carUid] - if (car != null) { - car.x = data.x - car.y = data.y - car.angle = data.angle - } - }) - } - } - - private fun handlerDebugMemoryResponse(message: DebugResponseMemoryStats) { - println("heapDynamicMaxBytes ${message.heapDynamicMaxBytes}") - println("heapDynamicTail ${message.heapDynamicTail}") - println("heapDynamicTotalBytes ${message.heapDynamicTotalBytes}") - println("heapStaticTail ${message.heapStaticTail}") - } - - private fun handlerSonarResponse(message: SonarResponse, angles: IntArray) { - println("request angles: ${Arrays.toString(angles)}") - println("distances from sonar: ${Arrays.toString(message.distances)}") - val algThread = DebugClInterface.algorithmThread - if (algThread == null) { - return - } - if (!algThread.isAlive) { - return - } - try { - DebugClInterface.exchanger.exchange(message.distances, 20, TimeUnit.SECONDS) - } catch (e: InterruptedException) { - println("interrupted before sending datas to algorithm!") - } catch (e: TimeoutException) { - } - } - - override fun channelRead0(ctx: ChannelHandlerContext?, msg: Any?) { - if (msg is DefaultHttpContent) { - // TODO: refactor variable names. It's hard as hell to read code with "contentBytes" and "content(!)s(!)Bytes" in lines of code. - val contentsBytes = msg.content() - contentBytes = ByteArray(contentsBytes.capacity()) - contentsBytes.readBytes(contentBytes) - } - } -} diff --git a/server/src/main/java/car/client/ClientInitializer.kt b/server/src/main/java/car/client/ClientInitializer.kt deleted file mode 100644 index e5a40583e37..00000000000 --- a/server/src/main/java/car/client/ClientInitializer.kt +++ /dev/null @@ -1,16 +0,0 @@ -package car.client - -import io.netty.channel.ChannelInitializer -import io.netty.channel.socket.SocketChannel -import io.netty.handler.codec.http.HttpClientCodec - -class ClientInitializer : ChannelInitializer { - - constructor() - - override fun initChannel(ch: SocketChannel) { - val p = ch.pipeline() - p.addLast(HttpClientCodec()) - p.addLast(ClientHandler()) - } -} \ No newline at end of file diff --git a/server/src/main/java/car/server/Handler.kt b/server/src/main/java/car/server/Handler.kt deleted file mode 100644 index d418e94210d..00000000000 --- a/server/src/main/java/car/server/Handler.kt +++ /dev/null @@ -1,60 +0,0 @@ -package car.server - -import CodedInputStream -import CodedOutputStream -import ConnectionRequest -import ConnectionResponse -import connectUrl -import io.netty.buffer.Unpooled -import io.netty.channel.ChannelFutureListener -import io.netty.channel.ChannelHandlerContext -import io.netty.channel.SimpleChannelInboundHandler -import io.netty.handler.codec.http.* -import objects.Environment - -class Handler : SimpleChannelInboundHandler() { - var url: String = "" - var contentBytes: ByteArray = ByteArray(0) - - override fun channelReadComplete(ctx: ChannelHandlerContext) { - var success = true - var answer = ByteArray(0) - when (url) { - connectUrl -> { - val data = ConnectionRequest.BuilderConnectionRequest(IntArray(0), 0).build() - data.mergeFrom(CodedInputStream(contentBytes)) - val ipStr = data.ipValues.map { elem -> elem.toString() }.reduce { elem1, elem2 -> elem1 + "." + elem2 } - val uid = Environment.connectCar(ipStr, data.port) - val responseObject = ConnectionResponse.BuilderConnectionResponse(uid, 0).build() - answer = ByteArray(responseObject.getSizeNoTag()) - responseObject.writeTo(CodedOutputStream(answer)) - } - else -> { - success = false - } - } - val responseStatus = if (success) HttpResponseStatus.OK else HttpResponseStatus.BAD_REQUEST - val response = DefaultFullHttpResponse(HttpVersion.HTTP_1_1, responseStatus, Unpooled.copiedBuffer(answer)) - response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()) - ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE) - } - - override fun channelRead0(ctx: ChannelHandlerContext, msg: Any) { - if (msg is HttpRequest) { - this.url = msg.uri() - } - - if (msg is DefaultHttpContent) { - val contentsBytes = msg.content() - contentBytes = ByteArray(contentsBytes.capacity()) - contentsBytes.readBytes(contentBytes) - } - } - - - override fun exceptionCaught(ctx: ChannelHandlerContext?, cause: Throwable?) { - println("exception") - cause?.printStackTrace() - ctx?.close() - } -} \ No newline at end of file diff --git a/server/src/main/java/net/Handler.kt b/server/src/main/java/net/Handler.kt new file mode 100644 index 00000000000..5bedf73120c --- /dev/null +++ b/server/src/main/java/net/Handler.kt @@ -0,0 +1,8 @@ +package net + + +interface Handler { + + fun execute(bytesFromClient: ByteArray): ByteArray + +} \ No newline at end of file diff --git a/server/src/main/java/net/Routes.kt b/server/src/main/java/net/Routes.kt new file mode 100644 index 00000000000..7d2350a3798 --- /dev/null +++ b/server/src/main/java/net/Routes.kt @@ -0,0 +1,9 @@ +package net + +object Routes { + val CHANGE_MODE_PATH = "/change-mode" + val GET_WAY_POINTS_PATH = "/get-waypoints" + val DIRECTION_ORDER_PATH = "/direction-order" + val GET_DEBUG_PATH = "/get-debug" + val CAR_CONNECT = "/connect" +} \ No newline at end of file diff --git a/server/src/main/java/net/ServerHandler.kt b/server/src/main/java/net/ServerHandler.kt new file mode 100644 index 00000000000..47436d827c6 --- /dev/null +++ b/server/src/main/java/net/ServerHandler.kt @@ -0,0 +1,59 @@ +package net + +import io.netty.buffer.Unpooled +import io.netty.channel.ChannelHandlerContext +import io.netty.channel.SimpleChannelInboundHandler +import io.netty.handler.codec.http.* +import io.netty.util.CharsetUtil +import java.util.* + +class ServerHandler(val handlers: Map) : SimpleChannelInboundHandler() { + + var contentBytes: ByteArray = ByteArray(0) + var path: String = "" + var method: HttpMethod? = null + + override fun channelReadComplete(ctx: ChannelHandlerContext) { + val handler = handlers[path] + if (handler == null) { + ctx.close() + return + } + val responseBytes = handler.execute(contentBytes) + val response = encodeProtoInHttpResponse(responseBytes) + ctx.writeAndFlush(response).addListener(io.netty.channel.ChannelFutureListener.CLOSE) + } + + private fun encodeProtoInHttpResponse(responseBytes: ByteArray): DefaultHttpResponse { + val response = DefaultFullHttpResponse( + HttpVersion.HTTP_1_1, + HttpResponseStatus.OK, + Unpooled.copiedBuffer(Base64.getEncoder().encodeToString(responseBytes), CharsetUtil.UTF_8) + ) + response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()) + response.headers().add("Access-Control-Allow-Origin", "*") + response.headers().add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS") + response.headers().add("Access-Control-Allow-Headers", "X-Requested-With, Content-Direction, Content-Length") + response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()) + return response + } + + override fun channelRead0(ctx: ChannelHandlerContext, msg: Any) { + if (msg is HttpRequest) { + path = msg.uri() + method = msg.method() + } + + if (msg is DefaultHttpContent) { + val contentBuffer = msg.content() + contentBytes = ByteArray(contentBuffer.capacity()) + contentBuffer.readBytes(contentBytes) + } + } + + override fun exceptionCaught(ctx: ChannelHandlerContext?, cause: Throwable?) { + println("exception") + cause?.printStackTrace() + ctx?.close() + } +} \ No newline at end of file diff --git a/server/src/main/java/net/car/Dropper.kt b/server/src/main/java/net/car/Dropper.kt new file mode 100644 index 00000000000..9f749e4e0fa --- /dev/null +++ b/server/src/main/java/net/car/Dropper.kt @@ -0,0 +1,41 @@ +package net.car + +import net.car.client.Client +import objects.Environment +import java.net.ConnectException +import kotlin.concurrent.thread + +object Dropper { + + fun createCarsDestroyThread(): Thread { + return thread(false, false, null, "dropCar", -1, getThreadCode()) + } + + private fun getThreadCode(): () -> Unit { + return { + var stopped = false + while (!stopped) { + synchronized(Environment, { + dropInactiveCar(Environment) + }) + try { + Thread.sleep(120000) + } catch (e: InterruptedException) { + println("thread for destroy cars stopped") + stopped = true + } + } + } + } + + private fun dropInactiveCar(environment: Environment) { + for (key in environment.map.keys) { + try { + val carValue = environment.map[key] ?: continue + carValue.carConnection.sendRequest(Client.Request.PING, ByteArray(0)) + } catch (e: ConnectException) { + environment.map.remove(key) + } + } + } +} \ No newline at end of file diff --git a/server/src/main/java/net/car/client/Client.kt b/server/src/main/java/net/car/client/Client.kt new file mode 100644 index 00000000000..7e9cf9c0937 --- /dev/null +++ b/server/src/main/java/net/car/client/Client.kt @@ -0,0 +1,32 @@ +package net.car.client + +import org.asynchttpclient.DefaultAsyncHttpClient +import org.asynchttpclient.ListenableFuture +import org.asynchttpclient.Response +import java.rmi.UnexpectedException + +object Client { + + private val client = DefaultAsyncHttpClient() + private val timeout = 5 * 60 * 60 * 1000 + + enum class Request(val url: String) { + CONNECT("connect"), + GET_LOCATION("getLocation"), + ROUTE("route"), + ROUTE_METRIC("routeMetric"), + SONAR("sonar"), + DEBUG_MEMORY("debug/memory"), + DEBUG_SONAR("debug/sonar"), + PING("ping"), + EXPLORE_ANGLE("sonarExplore"); + } + + fun shutDownClient() { + client.close() + } + + fun makeRequest(request: String, data: ByteArray): ListenableFuture = + client.preparePost(request).setBody(data).setRequestTimeout(timeout).execute() ?: throw UnexpectedException(request) + +} diff --git a/server/src/main/java/car/server/Initializer.kt b/server/src/main/java/net/car/server/Initializer.kt similarity index 76% rename from server/src/main/java/car/server/Initializer.kt rename to server/src/main/java/net/car/server/Initializer.kt index 5619e2afe7f..4cfaabff309 100644 --- a/server/src/main/java/car/server/Initializer.kt +++ b/server/src/main/java/net/car/server/Initializer.kt @@ -1,4 +1,4 @@ -package car.server +package net.car.server import io.netty.channel.ChannelInitializer import io.netty.channel.ChannelPipeline @@ -6,6 +6,9 @@ import io.netty.channel.socket.SocketChannel import io.netty.handler.codec.http.HttpServerCodec import io.netty.util.concurrent.DefaultEventExecutorGroup import io.netty.util.concurrent.EventExecutorGroup +import net.Routes +import net.ServerHandler +import net.car.server.handlers.CarConnection class Initializer : ChannelInitializer { @@ -19,6 +22,6 @@ class Initializer : ChannelInitializer { val p: ChannelPipeline = channel.pipeline() p.addLast(HttpServerCodec()) - p.addLast(group, Handler()) + p.addLast(group, ServerHandler(mapOf(Pair(Routes.CAR_CONNECT, CarConnection())))) } } \ No newline at end of file diff --git a/server/src/main/java/car/server/Server.kt b/server/src/main/java/net/car/server/Server.kt similarity index 81% rename from server/src/main/java/car/server/Server.kt rename to server/src/main/java/net/car/server/Server.kt index 30f7e3a6e79..ce1ef9c6a26 100644 --- a/server/src/main/java/car/server/Server.kt +++ b/server/src/main/java/net/car/server/Server.kt @@ -1,4 +1,4 @@ -package car.server +package net.car.server import io.netty.bootstrap.ServerBootstrap import io.netty.channel.nio.NioEventLoopGroup @@ -7,10 +7,13 @@ import kotlin.concurrent.thread object Server { + + private val DEFAULT_PORT = 7925 private val handlerThreadsCount: Int = 100 - fun getCarServerThread(carServerPort: Int): Thread { + + fun createCarServerThread(carServerPort: Int = DEFAULT_PORT): Thread { return thread(false, false, null, "carServer", -1, { - println("car server started") + println("net.car server started") val bossGroup = NioEventLoopGroup(1) val workerGroup = NioEventLoopGroup() val b = ServerBootstrap() @@ -23,7 +26,7 @@ object Server { val channel = b.bind(carServerPort).sync().channel() channel.closeFuture().sync() } catch (e: InterruptedException) { - println("car server stopped") + println("net.car server stopped") } finally { bossGroup.shutdownGracefully() workerGroup.shutdownGracefully() diff --git a/server/src/main/java/net/car/server/handlers/CarConnection.kt b/server/src/main/java/net/car/server/handlers/CarConnection.kt new file mode 100644 index 00000000000..50f04257d90 --- /dev/null +++ b/server/src/main/java/net/car/server/handlers/CarConnection.kt @@ -0,0 +1,22 @@ +package net.car.server.handlers + +import CodedInputStream +import CodedOutputStream +import ConnectionRequest +import ConnectionResponse +import net.Handler +import objects.Environment + +class CarConnection : Handler { + + override fun execute(bytesFromClient: ByteArray): ByteArray { + val data = ConnectionRequest.BuilderConnectionRequest(IntArray(0), 0).build() + data.mergeFrom(CodedInputStream(bytesFromClient)) + val ipStr = data.ipValues.map { elem -> elem.toString() }.reduce { elem1, elem2 -> elem1 + "." + elem2 } + val uid = Environment.connectCar(ipStr, data.port) + val responseObject = ConnectionResponse.BuilderConnectionResponse(uid, 0).build() + val result = ByteArray(responseObject.getSizeNoTag()) + responseObject.writeTo(CodedOutputStream(result)) + return result + } +} \ No newline at end of file diff --git a/server/src/main/java/web/server/Initializer.kt b/server/src/main/java/net/web/server/Initializer.kt similarity index 59% rename from server/src/main/java/web/server/Initializer.kt rename to server/src/main/java/net/web/server/Initializer.kt index 628c8416e93..b600621a00c 100644 --- a/server/src/main/java/web/server/Initializer.kt +++ b/server/src/main/java/net/web/server/Initializer.kt @@ -1,4 +1,4 @@ -package web.server +package net.web.server import io.netty.channel.ChannelInitializer import io.netty.channel.ChannelPipeline @@ -6,6 +6,11 @@ import io.netty.channel.socket.SocketChannel import io.netty.handler.codec.http.HttpServerCodec import io.netty.util.concurrent.DefaultEventExecutorGroup import io.netty.util.concurrent.EventExecutorGroup +import net.Routes +import net.ServerHandler +import net.web.server.handlers.ChangeMode +import net.web.server.handlers.DirectionOrder +import net.web.server.handlers.GetRoomModel class Initializer : ChannelInitializer { @@ -18,6 +23,9 @@ class Initializer : ChannelInitializer { override fun initChannel(channel: SocketChannel) { val p: ChannelPipeline = channel.pipeline() p.addLast(HttpServerCodec()) - p.addLast(group, Handler()) + p.addLast(group, ServerHandler(mapOf( + Pair(Routes.CHANGE_MODE_PATH, ChangeMode()), + Pair(Routes.GET_DEBUG_PATH, GetRoomModel()), + Pair(Routes.DIRECTION_ORDER_PATH, DirectionOrder())))) } } \ No newline at end of file diff --git a/server/src/main/java/web/server/Server.kt b/server/src/main/java/net/web/server/Server.kt similarity index 90% rename from server/src/main/java/web/server/Server.kt rename to server/src/main/java/net/web/server/Server.kt index 392adedb1d4..55df06c586e 100644 --- a/server/src/main/java/web/server/Server.kt +++ b/server/src/main/java/net/web/server/Server.kt @@ -1,4 +1,4 @@ -package web.server +package net.web.server import io.netty.bootstrap.ServerBootstrap import io.netty.channel.nio.NioEventLoopGroup @@ -6,6 +6,9 @@ import io.netty.channel.socket.nio.NioServerSocketChannel import kotlin.concurrent.thread object Server { + + private val DEFAULT_PORT = 7926 + enum class ServerMode { IDLE, MANUAL_MODE, @@ -19,7 +22,7 @@ object Server { ModeChange.Mode.PerimeterBuilding -> PERIMETER_BUILDING ModeChange.Mode.PerimeterDebug -> PERIMETER_DEBUG ModeChange.Mode.Idle -> IDLE - else -> throw IllegalArgumentException("Illegal argument when parsing ServerMode from Protobuf Mode") + else -> throw IllegalArgumentException("Illegal argument when parsing ServerMode from proto buf Mode") } } } @@ -33,7 +36,7 @@ object Server { serverMode = newMode } - fun getWebServerThread(webServerPort: Int): Thread { + fun createWebServerThread(webServerPort: Int = DEFAULT_PORT): Thread { return thread(false, false, null, "webServer", -1, { println("web server started") val bossGroup = NioEventLoopGroup(1) diff --git a/server/src/main/java/net/web/server/handlers/ChangeMode.kt b/server/src/main/java/net/web/server/handlers/ChangeMode.kt new file mode 100644 index 00000000000..0abb1ef8679 --- /dev/null +++ b/server/src/main/java/net/web/server/handlers/ChangeMode.kt @@ -0,0 +1,36 @@ +package net.web.server.handlers + +import net.web.server.Server +import java.util.* +import CodedInputStream +import CodedOutputStream +import GenericResponse +import net.Handler + +class ChangeMode : Handler { + + override fun execute(bytesFromClient: ByteArray): ByteArray { + val ins = CodedInputStream(Base64.getDecoder().decode(bytesFromClient)) + val request = ModeChange.BuilderModeChange(ModeChange.Mode.fromIntToMode(0)).parseFrom(ins) + val requestedMode = Server.ServerMode.fromProtoMode(request.newMode) + + if (Server.serverMode != Server.ServerMode.IDLE && requestedMode != Server.ServerMode.IDLE) { + println("Can't change server mode from ${Server.serverMode.toString()} to ${requestedMode.toString()}") + val protoResponse = GenericResponse.BuilderGenericResponse(Result.BuilderResult(1).build()).build() + return protoBufToBytes(protoResponse) + } + + // Change server mode + Server.changeMode(requestedMode) + + // Respond with "OK"-protoBuf + val protoResponse = GenericResponse.BuilderGenericResponse(Result.BuilderResult(0).build()).build() + return protoBufToBytes(protoResponse) + } + + private fun protoBufToBytes(protoMessage: GenericResponse): ByteArray { + val result = ByteArray(protoMessage.getSizeNoTag()) + protoMessage.writeTo(CodedOutputStream(result)) + return result + } +} \ No newline at end of file diff --git a/server/src/main/java/net/web/server/handlers/DirectionOrder.kt b/server/src/main/java/net/web/server/handlers/DirectionOrder.kt new file mode 100644 index 00000000000..b669730ffc9 --- /dev/null +++ b/server/src/main/java/net/web/server/handlers/DirectionOrder.kt @@ -0,0 +1,43 @@ +package net.web.server.handlers + +import CodedOutputStream +import CodedInputStream +import GenericResponse +import net.Handler +import net.web.server.Server + +class DirectionOrder : Handler { + + override fun execute(bytesFromClient: ByteArray): ByteArray { + if (Server.serverMode != Server.ServerMode.MANUAL_MODE) { + println("Can't execute move order when not in manual mode") + val protoResponse = GenericResponse.BuilderGenericResponse(Result.BuilderResult(1).build()).build() + return protoBufToBytes(protoResponse) + } + + val ins = CodedInputStream(bytesFromClient) + val order = DirectionRequest.BuilderDirectionRequest(DirectionRequest.Command.FORWARD, 0, false).parseFrom(ins) + if (order.stop) { + net.web.server.Server.changeMode(Server.ServerMode.IDLE) + val protoResponse = GenericResponse.BuilderGenericResponse(Result.BuilderResult(1).build()).build() + return protoBufToBytes(protoResponse) + } + + sendCarOrder(order.command) + + // TODO: should be done as callback after sending net.car order + // Send update back + return ByteArray(0) + } + + private fun protoBufToBytes(protoMessage: GenericResponse): ByteArray { + val result = ByteArray(protoMessage.getSizeNoTag()) + protoMessage.writeTo(CodedOutputStream(result)) + return result + } + + // TODO: stub!! + private fun sendCarOrder(cmd: DirectionRequest.Command) { + println("Sent order ${cmd.toString()}") + } +} \ No newline at end of file diff --git a/server/src/main/java/net/web/server/handlers/GetRoomModel.kt b/server/src/main/java/net/web/server/handlers/GetRoomModel.kt new file mode 100644 index 00000000000..073e493dcf0 --- /dev/null +++ b/server/src/main/java/net/web/server/handlers/GetRoomModel.kt @@ -0,0 +1,53 @@ +package net.web.server.handlers + +import CodedOutputStream +import DebugResponse +import algorithm.RoomModel +import net.Handler +import objects.Environment + + +class GetRoomModel : Handler { + + override fun execute(bytesFromClient: ByteArray): ByteArray { + val protoMessage = getDebugInfo() + val result = ByteArray(protoMessage.getSizeNoTag()) + protoMessage.writeTo(CodedOutputStream(result)) + return result + } + + private fun getDebugInfo(): DebugResponse { + + val points = RoomModel.walls.flatMap({ it.points }).toTypedArray() + + val begin_x = IntArray(points.size) + val begin_y = IntArray(points.size) + val end_x = IntArray(points.size) + val end_y = IntArray(points.size) + + val rawPoints = RoomModel.walls.flatMap({ it.rawPoints }).toTypedArray() + val pointsX = rawPoints.map { it.x.toInt() }.toIntArray() + val pointsY = rawPoints.map { it.y.toInt() }.toIntArray() + + for (i in 0..points.size - 2) { + val curPoint = points[i] + val nextPoint = points[i + 1] + begin_x[i] = (curPoint.x + 0.5).toInt() + begin_y[i] = (curPoint.y + 0.5).toInt() + + end_x[i] = (nextPoint.x + 0.5).toInt() + end_y[i] = (nextPoint.y + 0.5).toInt() + } + + val wallDistances = RoomModel.walls.filter { it.isFinished }.map { + val firstPoint = it.points.first() + val lastPoint = it.points.last() + val vector = algorithm.geometry.Vector(firstPoint, lastPoint) + vector.length().toInt() + }.toIntArray() + + val car = Environment.map.values.last() + return DebugResponse.BuilderDebugResponse(begin_x, begin_y, end_x, end_y, car.x.toInt(), car.y.toInt(), + car.angle.toInt(), pointsX, pointsY, wallDistances).build() + } +} diff --git a/server/src/main/java/objects/Car.kt b/server/src/main/java/objects/Car.kt index 3d475f05dfa..246fb73175c 100644 --- a/server/src/main/java/objects/Car.kt +++ b/server/src/main/java/objects/Car.kt @@ -1,26 +1,16 @@ package objects -class Car constructor(uid: Int, host: String, port: Int) { +class Car constructor(val uid: Int, host: String, port: Int) { - val uid: Int - val host: String - val port: Int + var x = 0.0 + var y = 0.0 + var angle = 0.0 - var lastAction: Long - - var x: Int - var y: Int - var angle: Int + val carConnection: CarConnection init { - this.uid = uid - this.host = host - this.port = port - x = 0 - y = 0 - angle = 0 - this.lastAction = System.currentTimeMillis() + carConnection = CarConnection(host, port) } override fun toString(): String { diff --git a/server/src/main/java/objects/CarConnection.kt b/server/src/main/java/objects/CarConnection.kt new file mode 100644 index 00000000000..1f8b5a18087 --- /dev/null +++ b/server/src/main/java/objects/CarConnection.kt @@ -0,0 +1,14 @@ +package objects + +import net.car.client.Client +import org.asynchttpclient.ListenableFuture +import org.asynchttpclient.Response + + +class CarConnection(val host: String, val port: Int) { + + fun sendRequest(request: Client.Request, data: ByteArray): ListenableFuture { + return Client.makeRequest("http://$host:$port/${request.url}", data) + } + +} \ No newline at end of file diff --git a/server/src/main/java/web/server/Constants.kt b/server/src/main/java/web/server/Constants.kt deleted file mode 100644 index 1b30e416652..00000000000 --- a/server/src/main/java/web/server/Constants.kt +++ /dev/null @@ -1,8 +0,0 @@ -package web.server - -object Constants { - val changeModeURL = "/change-mode" - val getWaypointsURL = "/get-waypoints" - val directionOrderURL = "/direction-order" - val getDebug = "/get-debug" -} \ No newline at end of file diff --git a/server/src/main/java/web/server/Handler.kt b/server/src/main/java/web/server/Handler.kt deleted file mode 100644 index 6d5b353fba4..00000000000 --- a/server/src/main/java/web/server/Handler.kt +++ /dev/null @@ -1,148 +0,0 @@ -package web.server - -import CodedInputStream -import CodedOutputStream -import DirectionRequest -import GenericResponse -import ModeChange -import Result -import Waypoints -import DebugResponse -import algorithm.RoomModel -import io.netty.buffer.Unpooled -import io.netty.channel.ChannelHandlerContext -import io.netty.channel.SimpleChannelInboundHandler -import io.netty.handler.codec.http.* -import io.netty.util.CharsetUtil -import java.util.* - -class Handler : SimpleChannelInboundHandler() { - - var contentBytes: ByteArray = ByteArray(0) - var url: String? = null - var method: HttpMethod? = null - - fun encodeProtoInHttpResponse(outs: CodedOutputStream): DefaultHttpResponse { - val response = DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, - HttpResponseStatus.OK, - Unpooled.copiedBuffer(Base64.getEncoder().encodeToString(outs.buffer), CharsetUtil.UTF_8) - ) - response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()) - response.headers().add("Access-Control-Allow-Origin", "*") - response.headers().add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS") - response.headers().add("Access-Control-Allow-Headers", "X-Requested-With, Content-Direction, Content-Length") - return response - } - - fun respondWith(ctx: ChannelHandlerContext, msg: GenericResponse) { - val outs = CodedOutputStream(ByteArray(msg.getSizeNoTag())) - msg.writeTo(outs) - val response = encodeProtoInHttpResponse(outs) - ctx.writeAndFlush(response).addListener(io.netty.channel.ChannelFutureListener.CLOSE) - } - - fun respondWith(ctx: ChannelHandlerContext, msg: Waypoints) { - val outs = CodedOutputStream(ByteArray(msg.getSizeNoTag())) - msg.writeTo(outs) - val response = encodeProtoInHttpResponse(outs) - ctx.writeAndFlush(response).addListener(io.netty.channel.ChannelFutureListener.CLOSE) - } - - fun respondWith(ctx: ChannelHandlerContext, msg: DebugResponse) { - val outs = CodedOutputStream(ByteArray(msg.getSizeNoTag())) - msg.writeTo(outs) - val response = encodeProtoInHttpResponse(outs) - ctx.writeAndFlush(response).addListener(io.netty.channel.ChannelFutureListener.CLOSE) - } - - override fun channelReadComplete(ctx: ChannelHandlerContext) { - when (url) { - Constants.changeModeURL -> { - // Parse mode change request - val ins = CodedInputStream(Base64.getDecoder().decode(contentBytes)) - val request = ModeChange.BuilderModeChange(ModeChange.Mode.fromIntToMode(0)).parseFrom(ins) - val requestedMode = Server.ServerMode.fromProtoMode(request.newMode) - - if (Server.serverMode != Server.ServerMode.IDLE && requestedMode != Server.ServerMode.IDLE) { - println("Can't change server mode from ${Server.serverMode.toString()} to ${requestedMode.toString()}") - val protoResponse = GenericResponse.BuilderGenericResponse(Result.BuilderResult(1).build()).build() - respondWith(ctx, protoResponse) - return - } - - // Change server mode - Server.changeMode(requestedMode) - - // Respond with "OK"-protobuf - val protoResponse = GenericResponse.BuilderGenericResponse(Result.BuilderResult(0).build()).build() - respondWith(ctx, protoResponse) - } - Constants.getWaypointsURL -> { - if (Server.serverMode != Server.ServerMode.PERIMETER_BUILDING) { - println("Can't get waypoints when in not Permiter Building mode") - val protoResponse = GenericResponse.BuilderGenericResponse(Result.BuilderResult(1).build()).build() - respondWith(ctx, protoResponse) - return - } - - // Send update for UI - val msg = RoomModel.getUpdate() - respondWith(ctx, msg) - } - Constants.directionOrderURL -> { - if (Server.serverMode != Server.ServerMode.MANUAL_MODE) { - println("Can't execute move order when not in manual mode") - val protoResponse = GenericResponse.BuilderGenericResponse(Result.BuilderResult(1).build()).build() - respondWith(ctx, protoResponse) - return - } - - // Parse direction order - val ins = CodedInputStream(contentBytes) - val order = DirectionRequest.BuilderDirectionRequest(DirectionRequest.Command.FORWARD, 0, false).parseFrom(ins) - if (order.stop) { - web.server.Server.changeMode(Server.ServerMode.IDLE) - val protoResponse = GenericResponse.BuilderGenericResponse(Result.BuilderResult(1).build()).build() - respondWith(ctx, protoResponse) - return - } - - sendCarOrder(order.command) - - // TODO: should be done as callback after sending car order - // Send update back - val msg = RoomModel.getUpdate() - respondWith(ctx, msg) - } - Constants.getDebug -> { - val msg = RoomModel.getDebugInfo() - respondWith(ctx, msg) - } - } - } - - // TODO: stub!! - fun sendCarOrder(cmd: DirectionRequest.Command) { - println("Sent order ${cmd.toString()}") - } - - override fun channelRead0(ctx: ChannelHandlerContext, msg: Any) { - if (msg is HttpRequest) { - url = msg.uri() - method = msg.method() - } - - if (msg is DefaultHttpContent) { - val contentBuffer = msg.content() - contentBytes = ByteArray(contentBuffer.capacity()) - contentBuffer.readBytes(contentBytes) - } - } - - override fun exceptionCaught(ctx: ChannelHandlerContext?, cause: Throwable?) { - println("exception") - cause?.printStackTrace() - ctx?.close() - } -} \ No newline at end of file