large refactoring of main server
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package net
|
||||
|
||||
|
||||
interface Handler {
|
||||
|
||||
fun execute(bytesFromClient: ByteArray): ByteArray
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
+5
-2
@@ -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()))))
|
||||
}
|
||||
}
|
||||
+7
-4
@@ -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
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -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()))))
|
||||
}
|
||||
}
|
||||
+6
-3
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user