Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
brienw committed Feb 23, 2018
0 parents commit 9631636
Show file tree
Hide file tree
Showing 14 changed files with 330 additions and 0 deletions.
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# The directory Mix will write compiled artifacts to.
/_build/

# If you run "mix test --cover", coverage assets end up here.
/cover/

# The directory Mix downloads your dependencies sources to.
/deps/

# Where 3rd-party dependencies like ExDoc output generated docs.
/doc/

# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch

# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump

# Also ignore archive artifacts (built via "mix archive.build").
*.ez
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Cand

**TODO: Add description**

```
{:ok, pid} = Cand.Socket.start_link
Cand.Protocol.connect(pid, {192,168,0,12}, 28600)
Cand.Protocol.open(pid, 'can0')
Cand.Protocol.rawmode(pid)
```

```
Cand.Protocol.send(pid, "295", 8, "64 6E 74 70 61 6E 69 63")
```

## Socket

connect(Socket, host, port)

connect(host, port)

{:ok, Socket} = Socket.connect(Socket, host, port)

{:ok, Socket} = Socket.connect(host, port)

## Protocol

connect(host, port)

open(pid, canbus)

send(pid, can_id, ...)

echo(pid)

rawmode(pid)

bcmmode(pid)
Binary file added cand.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config

# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
# file won't be loaded nor affect the parent project. For this reason,
# if you want to provide default values for your application for
# 3rd-party users, it should be done in your "mix.exs" file.

# You can configure your application as:
#
# config :cand, key: :value
#
# and access this configuration in your application as:
#
# Application.get_env(:cand, :key)
#
# You can also configure a 3rd-party app:
#
# config :logger, level: :info
#

# It is also possible to import configuration files, relative to this
# directory. For example, you can emulate configuration per environment
# by uncommenting the line below and defining dev.exs, test.exs and such.
# Configuration from the imported file will override the ones defined
# here (which is why it is important to import them last).
#
# import_config "#{Mix.env}.exs"
3 changes: 3 additions & 0 deletions lib/cand.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
defmodule Cand do
alias Cand.Socket
end
20 changes: 20 additions & 0 deletions lib/cand/application.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
defmodule Cand.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false

use Application

def start(_type, _args) do
# List all child processes to be supervised
children = [
# Starts a worker by calling: Cand.Worker.start_link(arg)
# {Cand.Worker, arg},
]

# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Cand.Supervisor]
Supervisor.start_link(children, opts)
end
end
10 changes: 10 additions & 0 deletions lib/cand/listener.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
defmodule Cand.Listener do
@moduledoc """
Message handler for TCP socket messages.
"""
@callback listen(arg ::any ) :: nil

# listen(:ok)
# listen({:error, message :: String.t})
# listen({:socket_closed, port :: port})
end
19 changes: 19 additions & 0 deletions lib/cand/listener/stdout.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
defmodule Cand.Listener.Stdout do
@behaviour Cand.Listener

def listen(:ok) do
IO.puts "OK"
end

def listen({:error, msg}) do
IO.puts "ERROR: #{msg}"
end

def listen({:socket_closed, port}) do
IO.puts "Socket Closed: #{port}"
end

def listen(msg) do
IO.puts msg
end
end
60 changes: 60 additions & 0 deletions lib/cand/protocol.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
defmodule Cand.Protocol do
@moduledoc """
...
"""
# use GenServer
alias Cand.Socket

@doc """
GenServer.init/1 callback
"""
def init(state), do: {:ok, state}

def connect(host, port, opts \\ [:binary, active: true]) do
{:ok, socket} = Socket.start_link
{:ok, _} = Socket.connect(socket, host, port, opts)
{:ok, socket}
end

def set_listener(socket, listener) do
Socket.listen(socket, listener)
end

def open(socket, can_device) do
Socket.send(socket, "< open #{can_device} >")
{:ok, can_device}
end

def ping(socket) do
Socket.send(socket, '< echo >')
{:ok, 'pong'}
end

def send(socket, can_id, can_dlc, data) do
Socket.send(socket, '< send #{can_id} #{can_dlc} #{data} >')
end

def send_integer(socket, can_id, int) when is_integer(int) do
enc_data = Integer.to_string(int, 16)
can_dlc = String.length(enc_data)
send(socket, can_id, can_dlc, enc_data)
end

def send_string(socket, can_id, data) do
enc_data = Base.encode16(data)
can_dlc = String.length(enc_data)
send(socket, can_id, can_dlc, enc_data)
end

## Mode ##

def bcmmode(socket) do
Socket.send(socket, '< bcmmode >')
end

def rawmode(socket) do
Socket.send(socket, '< rawmode >')
{:ok}
end

end
95 changes: 95 additions & 0 deletions lib/cand/socket.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
defmodule Cand.Socket do
alias Cand.Listener
@moduledoc """
TCP socket handler for socketcand endpoint.
"""
use GenServer

@initial_state %{socket: nil, listener: nil}

def init(state), do: {:ok, state}

def start_link do
GenServer.start_link(__MODULE__, @initial_state)
end

def connect(pid, host, port, opts \\ []) do
GenServer.call(pid, {:connect, host, port, opts})
end

def listen(pid, listener) do
GenServer.call(pid, {:listen, listener})
end

def receive(pid) do
GenServer.call(pid, {:recv})
end

def send(pid, msg) do
GenServer.call(pid, {:send, msg})
end

def send_receive(pid, msg) do
GenServer.call(pid, {:send_receive, msg})
end

def handle_call({:connect, host, port, opts}, _from_, state) do
listener = Keyword.get(opts, :listener, Listener.Stdout)
{:ok, socket} = :gen_tcp.connect(host, port, opts)
{:reply, {:ok, host: host, port: port, opts: opts}, %{state | socket: socket, listener: listener}}
end

def handle_call({:listen, listener}, _from_, state) do
{:reply, :ok, %{state | listener: listener}}
end

def handle_call({:recv}, _from_, %{socket: socket} = state) do
{:ok, msg} = :gen_tcp.recv(socket, 0)
{:reply, msg, state}
end

def handle_call({:send, msg}, _from_, %{socket: socket} = state) do
:ok = :gen_tcp.send(socket, msg)
{:reply, msg, state}
end

def handle_call({:send_receive, msg}, _from_, %{socket: socket} = state) do
:ok = :gen_tcp.send(socket, msg)
{:ok, response} = :gen_tcp.recv(socket, 0)
{:reply, response, state}
end

def handle_info({:tcp, _, message}, %{listener: listener} = state) do
message
|> List.to_string
|> parse_messages
|> Enum.map(fn(message) ->
listener.listen message
end)
{:noreply, state}
end

def handle_info({:tcp_closed, port}, %{listener: listener} = state) do
listener.listen {:socket_closed, port}
{:noreply, state}
end

defp parse_messages(messages) do
messages
|> String.split("><")
|> Enum.map(fn(frame) ->
frame
|> String.trim("<")
|> String.trim(">")
|> String.trim
|> parse_message
end)
end

defp parse_message("ok"), do: :ok
defp parse_message("hi"), do: :ok
defp parse_message("frame " <> frame), do: frame
defp parse_message("error " <> message), do: {:error, message}
defp parse_message(message), do: {:error, message}

end
28 changes: 28 additions & 0 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
defmodule Cand.Mixfile do
use Mix.Project

def project do
[
app: :cand,
version: "0.1.0",
build_path: "_build",
deps_path: "deps",
lockfile: "mix.lock",
elixir: "~> 1.5",
start_permanent: Mix.env == :prod,
deps: deps()
]
end

def application do
[
extra_applications: [:logger],
mod: {Cand.Application, []}
]
end

defp deps do
[
]
end
end
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
%{}
5 changes: 5 additions & 0 deletions test/cand_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
defmodule CandTest do
use ExUnit.Case
doctest Cand

end
1 change: 1 addition & 0 deletions test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ExUnit.start()

0 comments on commit 9631636

Please sign in to comment.