Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions clients/elixir_client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# elx_small_http_cowboy

Elixir client

Build:
> mix deps.get

Run:
> WORLD_NAME="hostname" SOLUTION_ID="123" iex -S mix
49 changes: 49 additions & 0 deletions clients/elixir_client/lib/elixir_client.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
defmodule ElixirClient do
use Application
import Supervisor.Spec

def start(_type, _args) do

host = case System.get_env()["WORLD_NAME"] do
nil -> fatal_error("env WORLD_NAME not set")
world_name -> world_name
end

solution_id = case System.get_env()["SOLUTION_ID"] do
nil -> fatal_error("env SOLUTION_ID not set")
str_solution_id ->
{solution_id, _} = Integer.parse(str_solution_id)
solution_id
end

IO.puts "Host: #{host}"
ip = case :inet.gethostbyname(String.to_char_list(host)) do
{:ok, {:hostent, _, [], :inet, 4, ip_list}} ->
ip_list
|> hd()
|> :inet_parse.ntoa()
|> to_string()
{:error, :nxdomain} ->
fatal_error("Can't determine host")
end

port = 8000

IO.puts "IPv4 address: #{inspect ip}"
IO.puts "Port: #{inspect port}"
IO.puts "SOLUTION_ID: #{inspect solution_id}"

childrens = [
worker(Strategy, []),
worker(ElixirClient.Api, []),
worker(ElixirClient.Client, [{host, port, solution_id}])
]

Supervisor.start_link(childrens, strategy: :one_for_one)
end

defp fatal_error(message) do
IO.puts(message)
throw(message)
end
end
105 changes: 105 additions & 0 deletions clients/elixir_client/lib/elixir_client/api.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
defmodule Elevator do
defstruct id: nil, y: nil, passengers: nil, state: nil,
speed: nil, floor: nil, next_floor: nil,
time_on_floor: nil, type: nil

use ExConstructor
# goToFloor
def go_to_floor(elevator, floor) do
cmd_map = %{command: "go_to_floor",
args: %{
elevator_id: elevator.id,
floor: floor
}}
ElixirClient.Api.add_command(cmd_map)
end
end

defmodule Passenger do
defstruct id: nil, elevator: nil, x: nil, y: nil, state: nil,
time_to_away: nil, from_floor: nil,
dest_floor: nil, type: nil, floor: nil

use ExConstructor
def has_elevator(passenger) do
passenger.elevator != nil
end

def set_elevator(passenger, elevator) do
cmd_map = %{command: "set_elevator_to_passenger",
args: %{
elevator_id: elevator.id,
passenger_id: passenger.id
}}
ElixirClient.Api.add_command(cmd_map)
end
end


defmodule ElixirClient.Api do
use GenServer
require Logger

def start_link() do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end

def init(:ok) do
# Logger.info "api init"
{:ok, %{commands: []}}
end

def generate_actions(json_actions) do
# Logger.info "api generate_actions"
:gen_server.cast(__MODULE__, {:generate_actions, json_actions})
end

def add_command(command) do
# Logger.info "api generate_actions"
:gen_server.cast(__MODULE__, {:add_command, command})
end

def get_state() do
# Logger.info "api generate_actions"
:gen_server.call(__MODULE__, :get_state)
end

def handle_cast({:generate_actions, json_actions}, state) do
# Logger.info "api generate_actions rcv json=#{inspect json_actions}"

# обработка входящих и генерация исходящих
my_elevators = ElixirClient.Api.parse_elevators(json_actions["my_elevators"])
my_passengers = ElixirClient.Api.parse_passengers(json_actions["my_passengers"])
enemy_elevators = ElixirClient.Api.parse_elevators(json_actions["enemy_elevators"])
enemy_passengers = ElixirClient.Api.parse_passengers(json_actions["enemy_passengers"])

Strategy.on_tick(my_elevators, my_passengers, enemy_elevators, enemy_passengers)

array_actions = Enum.reverse(state.commands)

# отправка
ElixirClient.Client.send_actions(array_actions)

{:noreply, %{state | commands: [] }}
end

def handle_cast({:add_command, command}, state) do
# Logger.info "api generate_actions rcv json=#{inspect json_actions}"

{:noreply, %{state | commands: [command] ++ state.commands}}
end

def handle_call(:get_state, _from, state) do
{:reply, Enum.reverse(state.commands), state}
end

def parse_elevators(data) do
Enum.map(data, fn(el) ->
new_el = Elevator.new(el)
%{new_el | passengers: parse_passengers(new_el.passengers)}
end)
end

def parse_passengers(data), do: Enum.map(data, &Passenger.new(&1))

end
94 changes: 94 additions & 0 deletions clients/elixir_client/lib/elixir_client/client.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
defmodule ElixirClient.Client do
use GenServer
require Logger

def start_link(options) do
GenServer.start_link(__MODULE__, options, name: __MODULE__)
end

def send_actions(json_actions) do
:gen_server.cast(__MODULE__, {:send_actions, json_actions})
end

# Server Callbacks

def init({host, port, solution_id} = options) do
Logger.info "client init #{inspect options}"

{:ok, socket} = :gen_tcp.connect(String.to_char_list(host),
port, [:binary, {:packet, :line}, {:active, true}])

json = Poison.encode!(%{solution_id: solution_id}) <> "\n"
msg = String.to_char_list(json)
:gen_tcp.send(socket, msg)

{:ok, %{options: options, socket: socket, transmitting: false, sdata: ""}}
end

def handle_cast({:send_actions, json_actions}, state) do
# Logger.info "client send_actions #{inspect json_actions}"
json = Poison.encode!(json_actions) <> "\n"
msg = String.to_char_list(json)
:gen_tcp.send(state.socket, msg)
{:noreply, state}
end

def handle_info({:tcp, _port, msg}, state) do
rcvd = state.sdata <> msg

case String.contains?(rcvd, "\n") do
true ->
array_of_json = String.split(rcvd, "\n")
# Logger.debug ">>>> array_of_json=#{inspect array_of_json}"
# spawn(__MODULE__, :parse_jsons, [array_of_json])
parse_jsons(array_of_json)

{:noreply, %{state | sdata: List.last(array_of_json) }}
_else ->
{:noreply, %{state | sdata: rcvd }}
end
end

def handle_info({:data_to_decode, data}, state) do
# Logger.info "handle_info data_to_decode data=#{inspect data}"
json = Poison.decode!(data)
handle_json(state, json)
# {:noreply, state}
end

def handle_info(_msg, state) do
# Logger.info "handle_info msg=#{inspect msg}"

{:noreply, state}
end

def parse_jsons([""]) do
""
end
def parse_jsons([not_ended_tail]) do
not_ended_tail
end
def parse_jsons([head | tail]) do
send(self(), {:data_to_decode, head})
parse_jsons(tail)
end

defp handle_json(state, %{"message" => "beginning"} = _json) do
Logger.info "client beginning"
{:noreply, %{state | transmitting: true}}
end

defp handle_json(state, %{"message" => "down"} = _json) do
Logger.info "client down"
:gen_tcp.close(state.socket)
{:noreply, %{state | socket: nil, transmitting: false}}
end

# сообщение не beginning и не down
defp handle_json(state, json) do
# Logger.info "client rcv json=#{inspect json}"
ElixirClient.Api.generate_actions(json)
{:noreply, state}
end

end
39 changes: 39 additions & 0 deletions clients/elixir_client/lib/strategy.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
defmodule Strategy do
# модуль стратегии - генсервер. на случай, если в стратегии
# захочется хранить какое-то состояние между тиками
use GenServer
require Logger

# Коды состояний лифтов ELEVATOR_STATE
@timeout_check
@waiting 0
@moving 1
@opening 2
@filling 3 # забирает и/или высаживает пассажиров
@closing 4

# Коды состояний пассажиров PASSENGER_STATE
@waiting_for_elevator 1 # ждет лифт
@moving_to_elevator 2 # идет к лифту
@returning 3 # возвращается обратно, если лифт уехал
@moving_to_floor 4 # идет по лестнице
@using_elevator 5 # едет в лифте
@exiting 6 # выходит из лифта

def on_tick(my_elevators, my_passengers, enemy_elevators, enemy_passengers) do
# Enum.map(my_elevators, fn(el) ->
# Elevator.go_to_floor(el, 1)
# end)
# hd(my_passengers)
# |> Passenger.set_elevator(hd(my_elevators))
end

def start_link() do
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end

def init(state) do
Logger.info "Strategy init state=#{inspect state}"
{:ok, state}
end
end
24 changes: 24 additions & 0 deletions clients/elixir_client/mix.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
defmodule ElixirClient.Mixfile do
use Mix.Project

def project do
[app: :elixir_client,
version: "0.1.0",
elixir: "~> 1.3",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps()]
end

def application do
[applications: [:logger, :exconstructor],
mod: {ElixirClient, []}]
end

defp deps do
[
{:poison, "~> 3.1"},
{:exconstructor, "~> 1.1.0"}
]
end
end
7 changes: 7 additions & 0 deletions clients/elixir_client/mix.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
%{"cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [], [], "hexpm"},
"exconstructor": {:hex, :exconstructor, "1.1.0", "272623a7b203cb2901c20cbb92c5c3ab103cc0087ff7c881979e046043346752", [], [], "hexpm"},
"mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [], [], "hexpm"},
"plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [], [], "hexpm"},
"ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [], [], "hexpm"}}
1 change: 1 addition & 0 deletions clients/elixir_client/priv/from_cli.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"args": {"elevator_id": 1, "floor": 1}, "command": "go_to_floor"}, {"args": {"elevator_id": 1, "floor": 1}, "command": "go_to_floor"}, {"args": {"elevator_id": 1, "floor": 6}, "command": "go_to_floor"}, {"args": {"elevator_id": 2, "floor": 1}, "command": "go_to_floor"}, {"args": {"elevator_id": 2, "floor": 1}, "command": "go_to_floor"}, {"args": {"elevator_id": 3, "floor": 1}, "command": "go_to_floor"}, {"args": {"elevator_id": 3, "floor": 1}, "command": "go_to_floor"}, {"args": {"elevator_id": 4, "floor": 1}, "command": "go_to_floor"}, {"args": {"elevator_id": 4, "floor": 1}, "command": "go_to_floor"}, {"args": {"passenger_id": 8, "elevator_id": 1}, "command": "set_elevator_to_passenger"}, {"args": {"passenger_id": 8, "elevator_id": 2}, "command": "set_elevator_to_passenger"}, {"args": {"passenger_id": 8, "elevator_id": 3}, "command": "set_elevator_to_passenger"}, {"args": {"passenger_id": 8, "elevator_id": 4}, "command": "set_elevator_to_passenger"}, {"args": {"passenger_id": 10, "elevator_id": 1}, "command": "set_elevator_to_passenger"}, {"args": {"passenger_id": 10, "elevator_id": 2}, "command": "set_elevator_to_passenger"}, {"args": {"passenger_id": 10, "elevator_id": 3}, "command": "set_elevator_to_passenger"}, {"args": {"passenger_id": 10, "elevator_id": 4}, "command": "set_elevator_to_passenger"}]
3 changes: 3 additions & 0 deletions clients/elixir_client/priv/from_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import json
js = [{u'args': {u'elevator_id': 1, u'floor': 1}, u'command': u'go_to_floor'}, {u'args': {u'elevator_id': 1, u'floor': 1}, u'command': u'go_to_floor'}, {u'args': {u'elevator_id': 1, u'floor': 6}, u'command': u'go_to_floor'}, {u'args': {u'elevator_id': 2, u'floor': 1}, u'command': u'go_to_floor'}, {u'args': {u'elevator_id': 2, u'floor': 1}, u'command': u'go_to_floor'}, {u'args': {u'elevator_id': 3, u'floor': 1}, u'command': u'go_to_floor'}, {u'args': {u'elevator_id': 3, u'floor': 1}, u'command': u'go_to_floor'}, {u'args': {u'elevator_id': 4, u'floor': 1}, u'command': u'go_to_floor'}, {u'args': {u'elevator_id': 4, u'floor': 1}, u'command': u'go_to_floor'}, {u'args': {u'passenger_id': 8, u'elevator_id': 1}, u'command': u'set_elevator_to_passenger'}, {u'args': {u'passenger_id': 8, u'elevator_id': 2}, u'command': u'set_elevator_to_passenger'}, {u'args': {u'passenger_id': 8, u'elevator_id': 3}, u'command': u'set_elevator_to_passenger'}, {u'args': {u'passenger_id': 8, u'elevator_id': 4}, u'command': u'set_elevator_to_passenger'}, {u'args': {u'passenger_id': 10, u'elevator_id': 1}, u'command': u'set_elevator_to_passenger'}, {u'args': {u'passenger_id': 10, u'elevator_id': 2}, u'command': u'set_elevator_to_passenger'}, {u'args': {u'passenger_id': 10, u'elevator_id': 3}, u'command': u'set_elevator_to_passenger'}, {u'args': {u'passenger_id': 10, u'elevator_id': 4}, u'command': u'set_elevator_to_passenger'}]
print json.dumps(js)
1 change: 1 addition & 0 deletions clients/elixir_client/priv/from_cli.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{u'args': {u'elevator_id': 1, u'floor': 1}, u'command': u'go_to_floor'}, {u'args': {u'elevator_id': 1, u'floor': 1}, u'command': u'go_to_floor'}, {u'args': {u'elevator_id': 1, u'floor': 6}, u'command': u'go_to_floor'}, {u'args': {u'elevator_id': 2, u'floor': 1}, u'command': u'go_to_floor'}, {u'args': {u'elevator_id': 2, u'floor': 1}, u'command': u'go_to_floor'}, {u'args': {u'elevator_id': 3, u'floor': 1}, u'command': u'go_to_floor'}, {u'args': {u'elevator_id': 3, u'floor': 1}, u'command': u'go_to_floor'}, {u'args': {u'elevator_id': 4, u'floor': 1}, u'command': u'go_to_floor'}, {u'args': {u'elevator_id': 4, u'floor': 1}, u'command': u'go_to_floor'}, {u'args': {u'passenger_id': 8, u'elevator_id': 1}, u'command': u'set_elevator_to_passenger'}, {u'args': {u'passenger_id': 8, u'elevator_id': 2}, u'command': u'set_elevator_to_passenger'}, {u'args': {u'passenger_id': 8, u'elevator_id': 3}, u'command': u'set_elevator_to_passenger'}, {u'args': {u'passenger_id': 8, u'elevator_id': 4}, u'command': u'set_elevator_to_passenger'}, {u'args': {u'passenger_id': 10, u'elevator_id': 1}, u'command': u'set_elevator_to_passenger'}, {u'args': {u'passenger_id': 10, u'elevator_id': 2}, u'command': u'set_elevator_to_passenger'}, {u'args': {u'passenger_id': 10, u'elevator_id': 3}, u'command': u'set_elevator_to_passenger'}, {u'args': {u'passenger_id': 10, u'elevator_id': 4}, u'command': u'set_elevator_to_passenger'}]
1 change: 1 addition & 0 deletions clients/elixir_client/priv/to_cli.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"my_elevators": [{"passengers": [{"dest_floor": 6, "state": 5, "floor": 1, "y": 1, "x": 60, "time_to_away": 418, "type": "FIRST_PLAYER", "from_floor": 1, "elevator": 1, "id": 1}, {"dest_floor": 2, "state": 5, "floor": 1, "y": 1, "x": 60, "time_to_away": 438, "type": "FIRST_PLAYER", "from_floor": 1, "elevator": 1, "id": 3}, {"dest_floor": 3, "state": 5, "floor": 1, "y": 1, "x": 60, "time_to_away": 458, "type": "FIRST_PLAYER", "from_floor": 1, "elevator": 1, "id": 5}], "state": 4, "time_on_floor": 85, "next_floor": 6, "floor": 1, "y": 1, "speed": 0.01557632741922111, "type": "SECOND_PLAYER", "id": 1}, {"passengers": [], "state": 3, "time_on_floor": 85, "next_floor": 1, "floor": 1, "y": 1, "speed": 0.02, "type": "SECOND_PLAYER", "id": 2}, {"passengers": [], "state": 3, "time_on_floor": 85, "next_floor": 1, "floor": 1, "y": 1, "speed": 0.02, "type": "SECOND_PLAYER", "id": 3}, {"passengers": [], "state": 3, "time_on_floor": 85, "next_floor": 1, "floor": 1, "y": 1, "speed": 0.02, "type": "SECOND_PLAYER", "id": 4}], "my_passengers": [{"dest_floor": 6, "state": 5, "floor": 1, "y": 1, "x": -60, "time_to_away": 418, "type": "SECOND_PLAYER", "from_floor": 1, "elevator": 1, "id": 2}, {"dest_floor": 2, "state": 5, "floor": 1, "y": 1, "x": -60, "time_to_away": 438, "type": "SECOND_PLAYER", "from_floor": 1, "elevator": 1, "id": 4}, {"dest_floor": 3, "state": 5, "floor": 1, "y": 1, "x": -60, "time_to_away": 458, "type": "SECOND_PLAYER", "from_floor": 1, "elevator": 1, "id": 6}, {"dest_floor": 5, "state": 3, "floor": 1, "y": 1, "x": -18, "time_to_away": 475, "type": "SECOND_PLAYER", "from_floor": 1, "elevator": null, "id": 8}, {"dest_floor": 7, "state": 2, "floor": 1, "y": 1, "x": 20, "time_to_away": 495, "type": "SECOND_PLAYER", "from_floor": 1, "elevator": 2, "id": 10}], "enemy_passengers": [{"dest_floor": 6, "state": 5, "floor": 1, "y": 1, "x": 60, "time_to_away": 418, "type": "FIRST_PLAYER", "from_floor": 1, "elevator": 1, "id": 1}, {"dest_floor": 2, "state": 5, "floor": 1, "y": 1, "x": 60, "time_to_away": 438, "type": "FIRST_PLAYER", "from_floor": 1, "elevator": 1, "id": 3}, {"dest_floor": 3, "state": 5, "floor": 1, "y": 1, "x": 60, "time_to_away": 458, "type": "FIRST_PLAYER", "from_floor": 1, "elevator": 1, "id": 5}, {"dest_floor": 5, "state": 3, "floor": 1, "y": 1, "x": 18, "time_to_away": 475, "type": "FIRST_PLAYER", "from_floor": 1, "elevator": null, "id": 7}, {"dest_floor": 7, "state": 2, "floor": 1, "y": 1, "x": -20, "time_to_away": 495, "type": "FIRST_PLAYER", "from_floor": 1, "elevator": 2, "id": 9}], "enemy_elevators": [{"passengers": [{"dest_floor": 6, "state": 5, "floor": 1, "y": 1, "x": -60, "time_to_away": 418, "type": "SECOND_PLAYER", "from_floor": 1, "elevator": 1, "id": 2}, {"dest_floor": 2, "state": 5, "floor": 1, "y": 1, "x": -60, "time_to_away": 438, "type": "SECOND_PLAYER", "from_floor": 1, "elevator": 1, "id": 4}, {"dest_floor": 3, "state": 5, "floor": 1, "y": 1, "x": -60, "time_to_away": 458, "type": "SECOND_PLAYER", "from_floor": 1, "elevator": 1, "id": 6}], "state": 4, "time_on_floor": 85, "next_floor": 6, "floor": 1, "y": 1, "speed": 0.01557632741922111, "type": "FIRST_PLAYER", "id": 1}, {"passengers": [], "state": 3, "time_on_floor": 85, "next_floor": 1, "floor": 1, "y": 1, "speed": 0.02, "type": "FIRST_PLAYER", "id": 2}, {"passengers": [], "state": 3, "time_on_floor": 85, "next_floor": 1, "floor": 1, "y": 1, "speed": 0.02, "type": "FIRST_PLAYER", "id": 3}, {"passengers": [], "state": 3, "time_on_floor": 85, "next_floor": 1, "floor": 1, "y": 1, "speed": 0.02, "type": "FIRST_PLAYER", "id": 4}]}
Loading