large refactoring of main server

This commit is contained in:
MaximZaitsev
2016-09-02 17:46:49 +03:00
parent 061646c5ff
commit 408aed5e90
33 changed files with 493 additions and 796 deletions
+27 -69
View File
@@ -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<IntArray>()
//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<String, Int> {
return mapOf(Pair("uid", id))
}
private fun executeRefreshLocationCommand() {
val cars = synchronized(Environment, { Environment.map.values })
val inactiveCars = mutableListOf<Int>()
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<Int>()
val angles = arrayListOf<Int>()
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
}
}
}
@@ -1,5 +0,0 @@
package Exceptions
class InactiveCarException : Exception() {
}
@@ -1,4 +0,0 @@
package Exceptions
class SonarDataException() : Exception() {
}
@@ -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<Double, Double>, 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<Double> {
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()
}
}
@@ -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))
@@ -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 <T> maxSuffix(first: List<T>, second: List<T>, distance: (T, T) -> Double): Int {
val distances = first.mapIndexed { i: Int, t: T ->
var score = 0.0
+8 -13
View File
@@ -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<String>) {
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<String>) {
}
}
//todo запуск потока алгоритма нужно вынести сюда,
//todo а debug интерфейсутребуется исключительно возможность установки кол-ва итераций
//CL user interface
DebugClInterface.run()
@@ -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<IntArray>) {
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<IntA
private var prevSonarDistances = mapOf<Angle, AngleData>()
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<IntA
.build()
val requestBytes = ByteArray(message.getSizeNoTag())
message.writeTo(CodedOutputStream(requestBytes))
val request = getDefaultHttpRequest(thisCar.host, sonarUrl, requestBytes)
try {
car.client.Client.sendRequest(request, thisCar.host, thisCar.port, mapOf(Pair("angles", anglesIntArray)))
} catch (e: InactiveCarException) {
val futureListener = thisCar.carConnection.sendRequest(Client.Request.SONAR, requestBytes)
val bytes = futureListener.get(300, TimeUnit.SECONDS).responseBodyAsBytes
val responseMessage = SonarResponse.BuilderSonarResponse(IntArray(0)).build()
responseMessage.mergeFrom(CodedInputStream(bytes))
return responseMessage.distances
} catch (e: ConnectException) {
println("connection error!")
}
try {
val distances = exchanger.exchange(IntArray(0), 300, TimeUnit.SECONDS)
return distances
} catch (e: TimeoutException) {
println("don't have response from car. Timeout!")
println("don't have response from net.car. Timeout!")
}
return IntArray(0)
}
//todo методы управления машинкой должны быть в отдельном классе по аналогии с carController
protected fun moveCar(message: RouteMetricRequest) {
val requestBytes = ByteArray(message.getSizeNoTag())
message.writeTo(CodedOutputStream(requestBytes))
@@ -84,23 +78,18 @@ abstract class AbstractAlgorithm(val thisCar: Car, val exchanger: Exchanger<IntA
private fun moveCar(messageBytes: ByteArray) {
val request = getDefaultHttpRequest(thisCar.host, setRouteMetricUrl, messageBytes)
try {
car.client.Client.sendRequest(request, thisCar.host, thisCar.port, mapOf<String, Int>())
} 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<IntA
this.requiredAngles = defaultAngles
val command: RouteMetricRequest
val state: CarState
try {
state = getCarState(anglesDistances)
command = getCommand(anglesDistances, state)
} catch (e: SonarDataException) {
Logger.log("iteration cancelled. need more data from sonar")
Logger.outdent()
Logger.log("============= FINISHING ITERATION ${iterationCounter} ============")
Logger.log("")
val state = getCarState(anglesDistances)
if (state == null) {
addCancelIterationToLog()
return
}
val command = getCommand(anglesDistances, state)
if (command == null) {
addCancelIterationToLog()
return
}
@@ -147,18 +135,17 @@ abstract class AbstractAlgorithm(val thisCar: Car, val exchanger: Exchanger<IntA
moveCar(command)
Logger.outdent()
Logger.log("============= FINISHING ITERATION ${iterationCounter} ============")
Logger.log("============= FINISHING ITERATION $iterationCounter ============")
Logger.log("")
}
protected fun getPrevState(): CarState? {
return prevState
private fun addCancelIterationToLog() {
Logger.log("iteration cancelled. need more data from sonar")
Logger.outdent()
Logger.log("============= FINISHING ITERATION $iterationCounter ============")
Logger.log("")
}
protected fun getPrevSonarDistances(): Map<Angle, AngleData> {
return prevSonarDistances
}
private fun getAngles(): Array<Angle> {
return requiredAngles
@@ -186,7 +173,7 @@ abstract class AbstractAlgorithm(val thisCar: Car, val exchanger: Exchanger<IntA
BACKWARD -> 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<IntA
}
protected fun rollback(steps: Int) {
Logger.log("=== Starting rollback for ${steps} steps ===")
Logger.log("=== Starting rollback for $steps steps ===")
Logger.indent()
var stepsRemaining = steps
while (stepsRemaining > 0 && history.size > 0) {
@@ -216,18 +203,9 @@ abstract class AbstractAlgorithm(val thisCar: Car, val exchanger: Exchanger<IntA
Logger.log("=== Finished rollback ===")
}
protected abstract fun getCarState(anglesDistances: Map<Angle, AngleData>): CarState
protected abstract fun getCommand(anglesDistances: Map<Angle, AngleData>, state: CarState): RouteMetricRequest
protected abstract fun getCarState(anglesDistances: Map<Angle, AngleData>): CarState?
protected abstract fun getCommand(anglesDistances: Map<Angle, AngleData>, 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
}
}
@@ -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<IntArray>) : 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<IntArray>) : Abs
private var isCompleted = false
private var circleFound = false
var carX = 0
var carY = 0
var carAngle = 0
override fun getCarState(anglesDistances: Map<Angle, AngleData>): CarState {
override fun getCarState(anglesDistances: Map<Angle, AngleData>): CarState? {
return CarState.WALL
}
@@ -45,9 +44,9 @@ class RoomBypassingAlgorithm(thisCar: Car, exchanger: Exchanger<IntArray>) : 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<IntArray>) : 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<IntArray>) : 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<IntArray>) : Abs
}
}
override fun getCommand(anglesDistances: Map<Angle, AngleData>, 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<Angle, AngleData>, 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<IntArray>) : 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<IntArray>) : 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<IntArray>) : 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<IntArray>) : 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<IntArray>) : 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
}
-105
View File
@@ -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<Point> {
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()
}
}
-54
View File
@@ -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<Int>()
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<Int>, 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)
}
}
}
}
@@ -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<Response>
= 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
}
}
-53
View File
@@ -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 <T> sendRequest(request: HttpRequest, host: String, port: Int, options: Map<String, T>) {
try {
bootstrap.attr(AttributeKey.valueOf<String>("url"), request.uri())
for ((key, value) in options) {
bootstrap.attr(AttributeKey.valueOf<T>(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<Response> =
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<String>("url"), "")
.attr(AttributeKey.newInstance<Int>("uid"), 0)
.attr(AttributeKey.newInstance<IntArray>("angles"), IntArray(0))
return b
}
}
@@ -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<Any>() {
var contentBytes: ByteArray = ByteArray(0)
override fun channelReadComplete(ctx: ChannelHandlerContext) {
val url = ctx.channel().attr(AttributeKey.valueOf<String>("url")).get()
when (url) {
getLocationUrl -> {
val carUid = ctx.channel().attr(AttributeKey.valueOf<Int>("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<IntArray>("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)
}
}
}
@@ -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<SocketChannel> {
constructor()
override fun initChannel(ch: SocketChannel) {
val p = ch.pipeline()
p.addLast(HttpClientCodec())
p.addLast(ClientHandler())
}
}
@@ -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<Any>() {
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()
}
}
+8
View File
@@ -0,0 +1,8 @@
package net
interface Handler {
fun execute(bytesFromClient: ByteArray): ByteArray
}
+9
View File
@@ -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"
}
+59
View File
@@ -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<String, Handler>) : SimpleChannelInboundHandler<Any>() {
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()
}
}
+41
View File
@@ -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)
}
}
}
}
@@ -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<Response> =
client.preparePost(request).setBody(data).setRequestTimeout(timeout).execute() ?: throw UnexpectedException(request)
}
@@ -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<SocketChannel> {
@@ -19,6 +22,6 @@ class Initializer : ChannelInitializer<SocketChannel> {
val p: ChannelPipeline = channel.pipeline()
p.addLast(HttpServerCodec())
p.addLast(group, Handler())
p.addLast(group, ServerHandler(mapOf(Pair(Routes.CAR_CONNECT, CarConnection()))))
}
}
@@ -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()
@@ -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
}
}
@@ -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<SocketChannel> {
@@ -18,6 +23,9 @@ class Initializer : ChannelInitializer<SocketChannel> {
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()))))
}
}
@@ -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)
@@ -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
}
}
@@ -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()}")
}
}
@@ -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()
}
}
+6 -16
View File
@@ -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 {
@@ -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<Response> {
return Client.makeRequest("http://$host:$port/${request.url}", data)
}
}
@@ -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"
}
-148
View File
@@ -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<Any>() {
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()
}
}