From aee08025ebab73599272042283d95902b08c1b6b Mon Sep 17 00:00:00 2001 From: dsavvinov Date: Thu, 25 Aug 2016 18:24:48 +0300 Subject: [PATCH] Added handling of web-requests in central server --- proto/protofiles_sources/compile_proto.sh | 7 +- .../server_client/cars.proto | 8 -- .../server_client/direction.proto | 7 +- server/.idea/modules/server.iml | 4 +- server/src/main/java/algorithm/RoomModel.kt | 32 ++++- .../src/main/java/car/client/ClientHandler.kt | 1 + server/src/main/java/web/server/Handler.kt | 109 ++++++++++++++++-- server/src/main/java/web/server/Server.kt | 23 ++++ 8 files changed, 163 insertions(+), 28 deletions(-) delete mode 100644 proto/protofiles_sources/server_client/cars.proto diff --git a/proto/protofiles_sources/compile_proto.sh b/proto/protofiles_sources/compile_proto.sh index f39ad34ab71..d6fa2ceb41c 100755 --- a/proto/protofiles_sources/compile_proto.sh +++ b/proto/protofiles_sources/compile_proto.sh @@ -1,5 +1,6 @@ #!/bin/bash -parent_path=$( cd "$(dirname "${BASH_SOURCE}")" ; pwd -P ) -cd "$parent_path" +PROTOC="../compiler/build/protoc" +cd "$(dirname "${BASH_SOURCE}")" mkdir out -p -../compiler/build/protoc --kotlin_out="./out/" --proto_path="./server_car" ./server_car/*.proto +${PROTOC} --kotlin_out="./out/" --proto_path="./server_car" ./server_car/*.proto +${PROTOC} --kotlin_out="./out/" --proto_path="./server_client" ./server_client/*.proto diff --git a/proto/protofiles_sources/server_client/cars.proto b/proto/protofiles_sources/server_client/cars.proto deleted file mode 100644 index a64d281e290..00000000000 --- a/proto/protofiles_sources/server_client/cars.proto +++ /dev/null @@ -1,8 +0,0 @@ -package carkot; - -option java_package = "client"; -option java_out_classname = "Car"; - -message car { - repeated string ids = 1; -} diff --git a/proto/protofiles_sources/server_client/direction.proto b/proto/protofiles_sources/server_client/direction.proto index de8567822b2..0debe83e1e6 100644 --- a/proto/protofiles_sources/server_client/direction.proto +++ b/proto/protofiles_sources/server_client/direction.proto @@ -11,8 +11,5 @@ message DirectionRequest { } Command command = 1; int32 sid = 2; -} - -message DirectionResponse { - int32 code = 1; -} + bool stop = 3; +} \ No newline at end of file diff --git a/server/.idea/modules/server.iml b/server/.idea/modules/server.iml index 8b292b004de..94a5e3f4ba6 100644 --- a/server/.idea/modules/server.iml +++ b/server/.idea/modules/server.iml @@ -20,8 +20,6 @@ - - @@ -31,5 +29,7 @@ + + \ No newline at end of file diff --git a/server/src/main/java/algorithm/RoomModel.kt b/server/src/main/java/algorithm/RoomModel.kt index b1af7a177e8..7364e18e8a3 100644 --- a/server/src/main/java/algorithm/RoomModel.kt +++ b/server/src/main/java/algorithm/RoomModel.kt @@ -1,5 +1,5 @@ package algorithm - +import Waypoints /** * Created by user on 8/23/16. */ @@ -7,4 +7,34 @@ object RoomModel { val point = arrayListOf>() + +// DEBUG BELOW + val rng = java.util.Random(42) + var iter = 0 + var currentPosition_x = 0 + var currentPosition_y = 0 + fun getUpdate(): Waypoints { + if (iter == 5) { + web.server.Server.changeMode(web.server.Server.ServerMode.IDLE) + return Waypoints.BuilderWaypoints(IntArray(0), IntArray(0), IntArray(0), IntArray(0), true).build() + } + + val begin_x = IntArray(10) + val begin_y = IntArray(10) + val end_x = IntArray(10) + val end_y = IntArray(10) + + for (i in 0..9) { + begin_x[i] = currentPosition_x + begin_y[i] = currentPosition_y + + currentPosition_x += rng.nextInt(3) + currentPosition_y += rng.nextInt(3) + + end_x[i] = currentPosition_x + end_y[i] = currentPosition_y + } + iter++ + return Waypoints.BuilderWaypoints(begin_x, begin_y, end_x, end_y, false).build() + } } \ No newline at end of file diff --git a/server/src/main/java/car/client/ClientHandler.kt b/server/src/main/java/car/client/ClientHandler.kt index c7b0f8ed7db..576482e26eb 100644 --- a/server/src/main/java/car/client/ClientHandler.kt +++ b/server/src/main/java/car/client/ClientHandler.kt @@ -98,6 +98,7 @@ class ClientHandler : SimpleChannelInboundHandler { override fun channelRead0(ctx: ChannelHandlerContext?, msg: Any?) { if (msg is DefaultHttpContent) { + // TODO: refactor variable names. It's hard as hell to read code with "contentBytes" and "content(!)s(!)Bytes" in lines of code. val contentsBytes = msg.content() contentBytes = ByteArray(contentsBytes.capacity()) contentsBytes.readBytes(contentBytes) diff --git a/server/src/main/java/web/server/Handler.kt b/server/src/main/java/web/server/Handler.kt index e76ff595a26..a334a0f4a76 100644 --- a/server/src/main/java/web/server/Handler.kt +++ b/server/src/main/java/web/server/Handler.kt @@ -5,26 +5,117 @@ import io.netty.channel.ChannelHandlerContext import io.netty.channel.SimpleChannelInboundHandler import io.netty.handler.codec.http.* import io.netty.util.CharsetUtil +import GenericResponse +import CodedOutputStream +import CodedInputStream +import java.util.* class Handler : SimpleChannelInboundHandler() { var contentBytes: ByteArray = ByteArray(0) + var url: String? = null + var method: HttpMethod? = null override fun channelReadComplete(ctx: ChannelHandlerContext) { + when (url) { + Constants.changeModeURL -> { + if (Server.serverMode != Server.ServerMode.IDLE && Server.serverMode != Server.ServerMode.MANUAL_MODE) { + throw IllegalStateException("Can't change server mode in algorithm execution mode!") + } - val response = DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer("" + Math.random() * 10000, CharsetUtil.UTF_8)) - response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()) - ctx.writeAndFlush(response).addListener(io.netty.channel.ChannelFutureListener.CLOSE) - } + // Parse mode change request + val ins = CodedInputStream(Base64.getDecoder().decode(contentBytes)) + val request = ModeChange.BuilderModeChange(ModeChange.Mode.ManualControl).parseFrom(ins) - override fun channelRead0(ctx: ChannelHandlerContext, msg: Any) { - if (msg is DefaultHttpContent) { - val contentsBytes = msg.content() - contentBytes = ByteArray(contentsBytes.capacity()) - contentsBytes.readBytes(contentBytes) + // Change server mode + Server.changeMode(Server.ServerMode.fromProtoMode(request.newMode)) + + // Respond with "OK"-protobuf + val protoResponse = GenericResponse.BuilderGenericResponse(Result.BuilderResult(0).build()) + val outs = CodedOutputStream(ByteArray(protoResponse.getSizeNoTag())) + protoResponse.writeTo(outs) + 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-Type, Content-Length"); + ctx.writeAndFlush(response).addListener(io.netty.channel.ChannelFutureListener.CLOSE) + } + Constants.getWaypointsURL -> { + if (Server.serverMode != Server.ServerMode.PERIMETER_BUILDING) { + throw IllegalStateException("Can't get waypoints when in not Permiter Building mode") + } + + // Build update for UI + val msg = RoomModel.getUpdate() + val outs = CodedOutputStream(ByteArray(msg.getSizeNoTag())) + msg.writeTo(outs) + 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-Type, Content-Length"); + ctx.writeAndFlush(response).addListener(io.netty.channel.ChannelFutureListener.CLOSE) + } + Constants.directionOrderURL -> { + if (Server.serverMode != Server.ServerMode.MANUAL_MODE) { + throw IllegalStateException("Can't execute move order when not in manual mode") + } + + // 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) + return + } + + sendCarOrder(order.command) + + // TODO: should be done as callback after sending car order + // Send update back + val msg = RoomModel.getUpdate() + val outs = CodedOutputStream(ByteArray(msg.getSizeNoTag())) + msg.writeTo(outs) + 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-Type, Content-Length"); + ctx.writeAndFlush(response).addListener(io.netty.channel.ChannelFutureListener.CLOSE) + } } } + // 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") diff --git a/server/src/main/java/web/server/Server.kt b/server/src/main/java/web/server/Server.kt index 00e2b665036..767d89626f5 100644 --- a/server/src/main/java/web/server/Server.kt +++ b/server/src/main/java/web/server/Server.kt @@ -6,7 +6,30 @@ import io.netty.channel.socket.nio.NioServerSocketChannel import kotlin.concurrent.thread object Server { + enum class ServerMode { + IDLE, + MANUAL_MODE, + PERIMETER_BUILDING; + + companion object { + fun fromProtoMode(mode: ModeChange.Mode): ServerMode { + return when (mode) { + ModeChange.Mode.ManualControl -> MANUAL_MODE + ModeChange.Mode.PerimeterBuilding -> PERIMETER_BUILDING + else -> throw IllegalArgumentException("Illegal argument when parsing ServerMode from Protobuf Mode") + } + } + } + } + private val handlerThreadsCount: Int = 10 + var serverMode = ServerMode.IDLE + + fun changeMode(newMode: ServerMode) { + println("Changing mode from ${serverMode.toString()} to ${newMode.toString()}") + serverMode = newMode + } + fun getWebServerThread(webServerPort: Int): Thread { return thread(false, false, null, "webServer", -1, { println("web server started")