Skip to content

Commit

Permalink
Add GQL subscriptions for barcode and config data
Browse files Browse the repository at this point in the history
  • Loading branch information
GregMefford committed Sep 3, 2018
1 parent e441a85 commit e90d28d
Show file tree
Hide file tree
Showing 13 changed files with 245 additions and 86 deletions.
15 changes: 14 additions & 1 deletion eye_ui/assets/css/app.css
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
/* This file is for your main application css. */
/* This file is for your main application css. */

body {
padding: 0 10%;
}

.container-fluid {
text-align: center;
}

#canvas {
background-size: contain;
background-repeat: no-repeat;
}
7 changes: 7 additions & 0 deletions eye_ui/assets/css/bootstrap_4.0.0.min.css

Large diffs are not rendered by default.

77 changes: 0 additions & 77 deletions eye_ui/assets/css/phoenix.css

This file was deleted.

98 changes: 97 additions & 1 deletion eye_ui/assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,107 @@
//
// If you no longer want to use a dependency, remember
// to also remove its path from "config.paths.watched".
import "phoenix_html"

// Import local files
//
// Local files can be imported directly using relative
// paths "./socket" or full ones "web/static/js/socket".

// import socket from "./socket"

import * as AbsintheSocket from "@absinthe/socket";
import {Socket as PhoenixSocket} from "phoenix";

const subscribe = (socket, onResult, operation) => {
const notifier = AbsintheSocket.send(socket, { operation });
AbsintheSocket.observe(socket, notifier, { onResult });
}

const phxSocket = new PhoenixSocket(`ws://${window.location.host}/socket`);
const absintheSocket = AbsintheSocket.create(phxSocket);

// Barcode Subscription

const barcodeOperation = `
subscription {
barcodes {
data
points {
x
y
}
}
}
`;

const onBarcodeData = data => {
const barcodes = data["data"]["barcodes"];
const dots = barcodes.map(barcode => {
const points = barcode["points"];
return barcode["points"].map(function(point, index) {
const x = point["x"];
const y = point["y"];
if (index == 0) {
return `
<circle cx="${x}" cy="${y}" r="3" stroke="red" fill="transparent" stroke-width="2"/>
<text x="${x + 5}" y="${y + 5}"><tspan font-weight="bold" fill="red">${barcode["data"]}</tspan></text>
`
} else {
return `<circle cx="${x}" cy="${y}" r="3" fill="red"/>`
}
}).join("\n");
}).join("\n");
document.getElementById("canvas").innerHTML = dots;
};

subscribe( absintheSocket, onBarcodeData, barcodeOperation );

// Configuration Subscription

const configOperation = `
subscription {
config {
size {
width
height
}
}
}
`;

const onConfigData = data => {
const {width, height} = data["data"]["config"]["size"];
document.getElementById("canvas").setAttribute("viewBox", `0 0 ${width} ${height}`);
};

subscribe( absintheSocket, onConfigData, configOperation );

// Resolution Buttons

const setResolution = (socket, width, height) => {
AbsintheSocket.send(socket, {
operation: `mutation {
size(width: ${width}, height: ${height}) {
size {
width
height
}
}
}`
});
};

document.getElementById("640x480").onclick = (event) => {
event.preventDefault();
setResolution(absintheSocket, 640, 480);
};

document.getElementById("1280x720").onclick = (event) => {
event.preventDefault();
setResolution(absintheSocket, 1280, 720);
};

document.getElementById("1920x1080").onclick = (event) => {
event.preventDefault();
setResolution(absintheSocket, 1920, 1080);
};
1 change: 1 addition & 0 deletions eye_ui/assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"watch": "brunch watch --stdin"
},
"dependencies": {
"@absinthe/socket-apollo-link": "^0.1.11",
"phoenix": "1.3.4"
},
"devDependencies": {
Expand Down
33 changes: 27 additions & 6 deletions eye_ui/assets/static/index.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Eye - Video Stream</title>
</head>
<body> <img src="video.mjpg" /> </body>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

<title>Eye - Video Stream</title>
<link rel="stylesheet" href="/css/app.css">
</head>

<body>
<div class="container-fluid">

<div class="btn-toolbar my-2" role="toolbar" aria-label="Toolbar">
<div class="btn-group mr-2" role="group" aria-label="Resolution Options">
<a class="btn btn-secondary" href="#" role="button" id="640x480">640 x 480</a>
<a class="btn btn-secondary" href="#" role="button" id="1280x720">1280 x 720</a>
<a class="btn btn-secondary" href="#" role="button" id="1920x1080">1920 x 1080</a>
</div>
</div> <!-- toolbar -->

<svg id="canvas" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720" style="background-image: url('video.mjpg');"/>

</div> <!-- container -->

<script src="/js/app.js"></script>
</body>
</html>
8 changes: 7 additions & 1 deletion eye_ui/lib/eye_ui/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ defmodule EyeUi.Application do
children = [
# Start the endpoint when the application starts
EyeUiWeb.Endpoint,
{Task.Supervisor, [name: EyeUi.TaskSupervisor]}
child_spec(Absinthe.Subscription, [EyeUiWeb.Endpoint]),
{Task.Supervisor, [name: EyeUi.TaskSupervisor]},
EyeUi.Publishers.Barcode
]

# See https://hexdocs.pm/elixir/Supervisor.html
Expand All @@ -18,6 +20,10 @@ defmodule EyeUi.Application do
Supervisor.start_link(children, opts)
end

defp child_spec(module, args \\ []) do
%{id: module, start: {module, :start_link, args}}
end

# Tell Phoenix to update the endpoint configuration
# whenever the application is updated.
def config_change(changed, _new, removed) do
Expand Down
7 changes: 7 additions & 0 deletions eye_ui/lib/eye_ui/publisher.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defmodule EyeUi.Publisher do

def publish(obj, topic) do
Absinthe.Subscription.publish(EyeUiWeb.Endpoint, obj, topic)
end

end
22 changes: 22 additions & 0 deletions eye_ui/lib/eye_ui/publishers/barcode.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule EyeUi.Publishers.Barcode do

require Logger

def child_spec(opts \\ []) do
%{
id: __MODULE__,
start: {__MODULE__, :start_link, opts}
}
end

def start_link do
Task.Supervisor.start_child(EyeUi.TaskSupervisor, & stream/0, restart: :permanent)
end

defp stream do
symbols = EyeUi.Resolvers.Barcode.get_next_scan()
Logger.debug(fn -> "Publishing barcode scans: #{inspect symbols}" end)
EyeUi.Publisher.publish(symbols, barcodes: "*")
stream()
end
end
21 changes: 21 additions & 0 deletions eye_ui/lib/eye_ui/resolvers/barcode.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
defmodule EyeUi.Resolvers.Barcode do

def get_next_scan(_parent, _args, _resolution) do
{:ok, get_next_scan()}
end

def get_next_scan do
Eye.BarcodeScanner.next_scan()
|> format_symbols()
end

defp format_symbols(nil), do: []
defp format_symbols(symbols), do: Enum.map(symbols, & format_symbol/1)

defp format_symbol(symbol), do: Map.update!(symbol, :points, & format_points/1)

defp format_points(points), do: Enum.map(points, & format_point/1)

defp format_point({x, y}), do: %{x: x, y: y}

end
24 changes: 24 additions & 0 deletions eye_ui/lib/eye_ui/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ defmodule EyeUi.Schema do

import_types Absinthe.Type.Custom
import_types EyeUi.Schema.CameraTypes
import_types EyeUi.Schema.BarcodeTypes

alias EyeUi.Resolvers

Expand All @@ -13,6 +14,11 @@ defmodule EyeUi.Schema do
resolve &Resolvers.Camera.get_config/3
end

@desc "Get barcode scans from the most recent video frame"
field :barcodes, list_of(:symbol) do
resolve &Resolvers.Barcode.get_next_scan/3
end

end

mutation name: "Mutation" do
Expand All @@ -26,4 +32,22 @@ defmodule EyeUi.Schema do

end

subscription name: "Subscription" do

field :barcodes, list_of(:symbol) do
config fn _args, _info ->
{:ok, topic: "*"}
end
end

field :config, :camera_config do
config fn _args, _info ->
{:ok, topic: "*"}
end

trigger :size, topic: fn _size -> "*" end
end

end

end
17 changes: 17 additions & 0 deletions eye_ui/lib/eye_ui/schema/barcode.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
defmodule EyeUi.Schema.BarcodeTypes do
use Absinthe.Schema.Notation

@desc "Barcode symbol"
object :symbol do
field :type, :string
field :quality, :integer
field :points, list_of(:point)
field :data, :string
end

@desc "The X and Y offset from the top-left pixel of the image"
object :point do
field :x, :integer
field :y, :integer
end
end
1 change: 1 addition & 0 deletions eye_ui/lib/eye_ui_web/channels/user_socket.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
defmodule EyeUiWeb.UserSocket do
use Phoenix.Socket
use Absinthe.Phoenix.Socket, schema: EyeUi.Schema

## Channels
# channel "room:*", EyeUiWeb.RoomChannel
Expand Down

0 comments on commit e90d28d

Please sign in to comment.