rfid 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/INSTALL.rdoc +55 -0
- data/README.rdoc +60 -0
- data/bin/rfid +18 -0
- data/bin/rfid_checkout +22 -0
- data/config.ru +9 -0
- data/config/boot.rb +6 -0
- data/config/environment.rb +8 -0
- data/config/rfid.yml +6 -0
- data/config/rfid.yml.sample +6 -0
- data/config/thin.yml +14 -0
- data/config/thin.yml.sample +14 -0
- data/lib/rfid.rb +75 -0
- data/lib/rfid/application.rb +115 -0
- data/lib/rfid/application/public/favicon.ico +0 -0
- data/lib/rfid/application/public/javascripts/application.js +12 -0
- data/lib/rfid/application/public/javascripts/debug.js +59 -0
- data/lib/rfid/application/public/stylesheets/application.css +49 -0
- data/lib/rfid/application/public/stylesheets/bootstrap.css +2467 -0
- data/lib/rfid/application/views/events.erb +34 -0
- data/lib/rfid/application/views/index.erb +21 -0
- data/lib/rfid/application/views/key_sets.erb +13 -0
- data/lib/rfid/application/views/key_string.erb +11 -0
- data/lib/rfid/application/views/keys.erb +20 -0
- data/lib/rfid/application/views/layout.erb +56 -0
- data/lib/rfid/application/views/redis.erb +16 -0
- data/lib/rfid/application/views/usage.erb +47 -0
- data/lib/rfid/cli.rb +78 -0
- data/lib/rfid/config.rb +23 -0
- data/lib/rfid/core_ext.rb +3 -0
- data/lib/rfid/core_ext/json_engine.rb +2 -0
- data/lib/rfid/event.rb +101 -0
- data/lib/rfid/translator.rb +68 -0
- data/lib/rfid/version.rb +3 -0
- data/lib/rfid/websocket.rb +35 -0
- data/lib/runner.rb +18 -0
- data/lib/server.rb +19 -0
- data/lib/templates/init +109 -0
- data/spec/models/application_spec.rb +54 -0
- data/spec/models/config_spec.rb +33 -0
- data/spec/models/event_spec.rb +22 -0
- data/spec/rfid_spec.rb +58 -0
- data/spec/spec_helper.rb +25 -0
- metadata +228 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
<div class="page-header">
|
2
|
+
<h1>Create new event</h1>
|
3
|
+
</div>
|
4
|
+
|
5
|
+
<% if params[:state] == 'success' %>
|
6
|
+
<div class="alert-message success">
|
7
|
+
<a href="javascript:void(0);" class="close" onclick="$(this).parents('div.alert-message').hide();">×</a>
|
8
|
+
<p><strong>Well done!</strong> You successfully create websocket event.</p>
|
9
|
+
</div>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<form action="/events" method="post" class="form-stacked">
|
13
|
+
<fieldset>
|
14
|
+
<div class="clearfix">
|
15
|
+
<label for="event_name">Name</label>
|
16
|
+
<div class="input">
|
17
|
+
<input type="text" size="30" name="event[name]" id="event_name" class="xxlarge">
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<div class="clearfix">
|
22
|
+
<label for="event_data">Data</label>
|
23
|
+
<div class="input">
|
24
|
+
<textarea rows="3" name="event[data]" id="event_data" class="xxlarge"></textarea>
|
25
|
+
<span class="help-block">
|
26
|
+
In JSON format. e.g. "{"name": "Joe", "message_count": 23}".
|
27
|
+
</span>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
<div class="actions">
|
31
|
+
<input type="submit" value="Create" class="btn primary"> <button class="btn" type="reset">Cancel</button>
|
32
|
+
</div>
|
33
|
+
</fieldset>
|
34
|
+
</form>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<script type="text/javascript" charset="utf-8">
|
2
|
+
jsload('websocket', "https://rfidapi.aimbulance.com/js/websocket.js");
|
3
|
+
jsload('debug', "/javascripts/debug.js");
|
4
|
+
</script>
|
5
|
+
|
6
|
+
<div class="page-header">
|
7
|
+
<h1>RFID <small>Debug console</small></h1>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<table id="debug_console" class="bordered-table zebra-striped">
|
11
|
+
<thead>
|
12
|
+
<tr>
|
13
|
+
<th>Type</th>
|
14
|
+
<th>Socket</th>
|
15
|
+
<th>Details</th>
|
16
|
+
<th>Time</th>
|
17
|
+
</tr>
|
18
|
+
</thead>
|
19
|
+
<tbody>
|
20
|
+
</tbody>
|
21
|
+
</table>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<div class="page-header">
|
2
|
+
<h1>Key "<%= @key %>" is a <%= rfid.redis.type @key %> <small>size: <%= redis_get_size(@key) %></small></h1>
|
3
|
+
</div>
|
4
|
+
|
5
|
+
<table class="bordered-table zebra-striped">
|
6
|
+
<% for row in redis_get_value_as_array(@key) %>
|
7
|
+
<tr>
|
8
|
+
<td>
|
9
|
+
<%= row %>
|
10
|
+
</td>
|
11
|
+
</tr>
|
12
|
+
<% end %>
|
13
|
+
</table>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<div class="page-header">
|
2
|
+
<h1>Key "<%= @key %>" is a <%= rfid.redis.type @key %> <small>size: <%= redis_get_size(@key) %></small></h1>
|
3
|
+
</div>
|
4
|
+
|
5
|
+
<table class="bordered-table zebra-striped">
|
6
|
+
<tr>
|
7
|
+
<td>
|
8
|
+
<%= redis_get_value_as_array(@key) %>
|
9
|
+
</td>
|
10
|
+
</tr>
|
11
|
+
</table>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<div class="page-header">
|
2
|
+
<h1>Keys owned by <%= rfid %> <small>All keys are actually prefixed with "rfid:"</small></h1>
|
3
|
+
</div>
|
4
|
+
|
5
|
+
<table class="bordered-table zebra-striped">
|
6
|
+
<tr>
|
7
|
+
<th>key</th>
|
8
|
+
<th>type</th>
|
9
|
+
<th>size</th>
|
10
|
+
</tr>
|
11
|
+
<% for key in rfid.keys.sort %>
|
12
|
+
<tr>
|
13
|
+
<th>
|
14
|
+
<a href="<%= "/keys/#{key}" %>"><%= key %></a>
|
15
|
+
</th>
|
16
|
+
<td><%= rfid.redis.type key %></td>
|
17
|
+
<td><%= redis_get_size key %></td>
|
18
|
+
</tr>
|
19
|
+
<% end %>
|
20
|
+
</table>
|
@@ -0,0 +1,56 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<title>RFID websocket status</title>
|
6
|
+
<meta name="description" content="RFID websocket">
|
7
|
+
<meta name="author" content="Aimbulance">
|
8
|
+
|
9
|
+
<!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
|
10
|
+
<!--[if lt IE 9]>
|
11
|
+
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
12
|
+
<![endif]-->
|
13
|
+
|
14
|
+
<!-- Le styles -->
|
15
|
+
<link rel="stylesheet" type="text/css" href="/stylesheets/bootstrap.css">
|
16
|
+
<link rel="stylesheet" type="text/css" href="/stylesheets/application.css">
|
17
|
+
<link rel="shortcut icon" href="/favicon.ico">
|
18
|
+
|
19
|
+
<script src="https://www.google.com/jsapi" type="text/javascript"></script>
|
20
|
+
<script language="Javascript" type="text/javascript">
|
21
|
+
//<![CDATA[
|
22
|
+
google.load("jquery", "1.7.0");
|
23
|
+
//]]>
|
24
|
+
</script>
|
25
|
+
<script src="/javascripts/application.js" type="text/javascript"></script>
|
26
|
+
</head>
|
27
|
+
<body>
|
28
|
+
|
29
|
+
<div class="topbar">
|
30
|
+
<div class="fill">
|
31
|
+
<div class="container">
|
32
|
+
<a class="brand" href="/">RFID WebSockets</a>
|
33
|
+
|
34
|
+
<ul class="nav">
|
35
|
+
<% ["Events", "Redis", "Keys", "Usage"].each do |name| %>
|
36
|
+
<%= tab name %>
|
37
|
+
<% end %>
|
38
|
+
</ul>
|
39
|
+
|
40
|
+
<ul class="nav secondary-nav">
|
41
|
+
<li><a href="javascript:void(0);" onclick="window.location.reload();">Refresh</a></li>
|
42
|
+
</ul>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
|
47
|
+
<div class="container">
|
48
|
+
<div class="content">
|
49
|
+
<%= yield %>
|
50
|
+
</div>
|
51
|
+
<footer>
|
52
|
+
<p>© 2011 Aimbulance. All rights reserved.</p>
|
53
|
+
</footer>
|
54
|
+
</div>
|
55
|
+
</body>
|
56
|
+
</html>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<div class="page-header">
|
2
|
+
<h1><%= rfid.redis_id %> <small>connection</small></h1>
|
3
|
+
</div>
|
4
|
+
|
5
|
+
<table class="bordered-table zebra-striped">
|
6
|
+
<% for key, value in rfid.redis.info.to_a.sort_by { |i| i[0].to_s } %>
|
7
|
+
<tr>
|
8
|
+
<th>
|
9
|
+
<%= key %>
|
10
|
+
</th>
|
11
|
+
<td>
|
12
|
+
<%= value %>
|
13
|
+
</td>
|
14
|
+
</tr>
|
15
|
+
<% end %>
|
16
|
+
</table>
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<div class="page-header">
|
2
|
+
<h1>Install instructions</h1>
|
3
|
+
</div>
|
4
|
+
|
5
|
+
<h3>Include javascripts</h3>
|
6
|
+
Simple:
|
7
|
+
<pre class="prettyprint">
|
8
|
+
<script src="/javascripts/websocket.js" type="text/javascript"></script>
|
9
|
+
</pre>
|
10
|
+
|
11
|
+
Dynamic:
|
12
|
+
<pre class="prettyprint">
|
13
|
+
(function(d, s, id) {
|
14
|
+
var js, fjs = d.getElementsByTagName(s)[0];
|
15
|
+
if (d.getElementById(id)) {return;}
|
16
|
+
js = d.createElement(s); js.id = id;
|
17
|
+
js.src = "/javascripts/websocket.js";
|
18
|
+
// Setup a callback to track once the script loads.
|
19
|
+
js.onload = subscribe;
|
20
|
+
fjs.parentNode.insertBefore(js, fjs);
|
21
|
+
}(document, 'script', 'rfid-websocket'));
|
22
|
+
|
23
|
+
function subscribe() {
|
24
|
+
...
|
25
|
+
}
|
26
|
+
</pre>
|
27
|
+
|
28
|
+
<h3>Subscribe to channel</h3>
|
29
|
+
<pre class="prettyprint">
|
30
|
+
var channel = new FancyWebSocket("ws://127.0.0.1:8080", { debug: true });
|
31
|
+
|
32
|
+
channel.bind('connect', function(data){
|
33
|
+
console.log(data);
|
34
|
+
});
|
35
|
+
|
36
|
+
channel.bind('checkout', function(data){
|
37
|
+
console.log(this.socket_id);
|
38
|
+
});
|
39
|
+
|
40
|
+
channel.bind('open', function(data){
|
41
|
+
// open
|
42
|
+
});
|
43
|
+
|
44
|
+
channel.bind('close', function(data){
|
45
|
+
// close
|
46
|
+
});
|
47
|
+
</pre>
|
data/lib/rfid/cli.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require "thor"
|
2
|
+
|
3
|
+
module Rfid
|
4
|
+
class CLI < Thor
|
5
|
+
include Thor::Actions
|
6
|
+
|
7
|
+
default_task :help
|
8
|
+
|
9
|
+
desc "websocket [ACTION]", "Action: start/stop/restart/run websocket server. For development pass run"
|
10
|
+
def websocket(method_name = 'start')
|
11
|
+
command = File.join(self.class.source_root, 'lib/runner.rb')
|
12
|
+
run_ruby_script "#{command} #{method_name}", :verbose => false
|
13
|
+
end
|
14
|
+
map %w(-w --websocket) => :websocket
|
15
|
+
|
16
|
+
desc "app [ACTION]", "Action: start/stop/restart webui for websocket server"
|
17
|
+
def app(method_name = 'start')
|
18
|
+
path = self.class.source_root
|
19
|
+
config = File.join(path, 'config/thin.yml')
|
20
|
+
rack = File.join(path, 'config.ru')
|
21
|
+
|
22
|
+
in_root do
|
23
|
+
run "thin #{method_name} -R #{rack} -C #{config} --chdir #{path}", :verbose => false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
map %w(-a --app) => :app
|
27
|
+
|
28
|
+
desc "checkout [DEVICE] [CARD1] [CARD2] ...", "Create RFID API event and send callback data to websocket server"
|
29
|
+
def checkout(device, *cards)
|
30
|
+
event = Rfid::Event.new(device, cards)
|
31
|
+
say "Create new event on device #{event.device_id} (#{device})", :green
|
32
|
+
event.save
|
33
|
+
end
|
34
|
+
map %w(-c --checkout) => :checkout
|
35
|
+
|
36
|
+
desc "setup", "Configure RFID options and create tmp directories"
|
37
|
+
method_option :force, :type => :boolean, :aliases => "-f", :banner => "Reset settings to defaults"
|
38
|
+
def setup
|
39
|
+
require 'yaml/store'
|
40
|
+
|
41
|
+
if options[:force]
|
42
|
+
say("Reset settings to defaults", :red)
|
43
|
+
copy_file("config/rfid.yml.sample", File.join(self.class.source_root, "config/rfid.yml"))
|
44
|
+
end
|
45
|
+
|
46
|
+
say("Press ENTER if you don't want change current value", :green)
|
47
|
+
|
48
|
+
store = YAML::Store.new(Rfid.config_path)
|
49
|
+
|
50
|
+
store.transaction do
|
51
|
+
Rfid.config.table.each do |key, value|
|
52
|
+
value = ask("#{key} (#{value}):", :yellow)
|
53
|
+
|
54
|
+
unless value.blank?
|
55
|
+
store[key.to_s] = value
|
56
|
+
say_status :set, "#{key} => #{value}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
say("Setup init script", :green)
|
62
|
+
copy_file "lib/templates/init", "/etc/init.d/rfid"
|
63
|
+
chmod "/etc/init.d/rfid", 0755
|
64
|
+
|
65
|
+
say("Setup completed", :green)
|
66
|
+
end
|
67
|
+
|
68
|
+
desc "version", "Prints the rfid's version information"
|
69
|
+
def version
|
70
|
+
say "Rfid version #{Rfid::VERSION}"
|
71
|
+
end
|
72
|
+
map %w(-v --version) => :version
|
73
|
+
|
74
|
+
def self.source_root
|
75
|
+
File.dirname(File.expand_path('../../', __FILE__))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/rfid/config.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "ostruct"
|
3
|
+
|
4
|
+
module Rfid
|
5
|
+
class Config < OpenStruct
|
6
|
+
attr_reader :table, :filepath
|
7
|
+
|
8
|
+
def initialize(filepath)
|
9
|
+
@filepath = filepath.to_s.strip
|
10
|
+
super(parse_file)
|
11
|
+
end
|
12
|
+
|
13
|
+
def reload!
|
14
|
+
@table = parse_file.symbolize_keys
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def parse_file
|
20
|
+
YAML.load_file(@filepath)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/rfid/event.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'curb'
|
2
|
+
require 'digest/md5'
|
3
|
+
require 'active_support/json'
|
4
|
+
|
5
|
+
module Rfid
|
6
|
+
class Event
|
7
|
+
attr_accessor :device_id, :key, :cards
|
8
|
+
|
9
|
+
def initialize(device, cards)
|
10
|
+
@key = device.to_s.strip
|
11
|
+
@device_id = (Rfid.config.device_id || @key)
|
12
|
+
@cards = Array.wrap(cards)
|
13
|
+
@url = File.join(Rfid.config.api_url, "/events/#{@device_id}.json")
|
14
|
+
@time = Time.now.strftime("%Y%m%d%H%M%S")
|
15
|
+
@mode = 1
|
16
|
+
end
|
17
|
+
|
18
|
+
def attributes
|
19
|
+
{
|
20
|
+
:device_id => @device_id,
|
21
|
+
:cards => @cards,
|
22
|
+
:time => @time,
|
23
|
+
:mode => @mode,
|
24
|
+
:url => @url,
|
25
|
+
:key => @key
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def save
|
30
|
+
Rfid.push_event({:event => 'checkout', :data => attributes})
|
31
|
+
|
32
|
+
curl = Curl::Easy.new(@url) do |c|
|
33
|
+
c.headers["User-Agent"] = "rfid-gem-#{Rfid::VERSION}"
|
34
|
+
c.verbose = true
|
35
|
+
c.timeout = 25
|
36
|
+
end
|
37
|
+
|
38
|
+
body = @cards.inject([]) do |arr, card|
|
39
|
+
arr << Curl::PostField.content('event[cards][]', card)
|
40
|
+
arr
|
41
|
+
end
|
42
|
+
|
43
|
+
body << Curl::PostField.content('event[mode]', @mode)
|
44
|
+
body << Curl::PostField.content('event[time]', @time)
|
45
|
+
body << Curl::PostField.content('event[hash]', hash)
|
46
|
+
|
47
|
+
begin
|
48
|
+
curl.http_post(*body)
|
49
|
+
parse(curl.response_code, curl.body_str)
|
50
|
+
rescue Exception => e
|
51
|
+
error!(e.message, e.backtrace.join("\n"))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_s
|
56
|
+
"<Event device: #{@device_id}, cards: #{@cards.join(',')}, time: #{@time}>"
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
# Generate md5 hash
|
62
|
+
def hash
|
63
|
+
value = cards.map(&:to_s).sort.join
|
64
|
+
secret_hash(value, @time)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Generate md5 sum
|
68
|
+
# md5(id, secret_key, cards.sort.join, time)
|
69
|
+
#
|
70
|
+
def secret_hash(*args)
|
71
|
+
Digest::MD5.hexdigest([device_id, secret_token].concat(args).map(&:to_s).join)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Get device secret_token
|
75
|
+
def secret_token
|
76
|
+
Rfid.config.device_token
|
77
|
+
end
|
78
|
+
|
79
|
+
# Save error in logs end send data to websocket
|
80
|
+
def error!(message, backtrace = nil)
|
81
|
+
Rfid.log("Error in", to_s, message, backtrace)
|
82
|
+
Rfid.push_event({:event => 'error', :data => { :message => message, :event => attributes }})
|
83
|
+
end
|
84
|
+
|
85
|
+
# Parse response end send it to websocket
|
86
|
+
def parse(status, body)
|
87
|
+
case status
|
88
|
+
when 200, 201 then
|
89
|
+
hash = ActiveSupport::JSON.decode(body)
|
90
|
+
Rfid.push_event(hash['data'])
|
91
|
+
when 422 then
|
92
|
+
hash = ActiveSupport::JSON.decode(body)
|
93
|
+
error!(hash['errors'])
|
94
|
+
when 500 then
|
95
|
+
error!("RFID Server API error: 500")
|
96
|
+
else
|
97
|
+
error!(body)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
2
|
+
require 'em-hiredis'
|
3
|
+
|
4
|
+
module Rfid
|
5
|
+
module Translator
|
6
|
+
extend self
|
7
|
+
|
8
|
+
mattr_accessor :channel
|
9
|
+
@@channel = nil
|
10
|
+
|
11
|
+
def redis
|
12
|
+
@redis ||= EM::Hiredis.connect(Rfid.config.redis_url)
|
13
|
+
end
|
14
|
+
|
15
|
+
def start
|
16
|
+
redis.blpop(Rfid.config.redis_key, 0).callback do |list, data|
|
17
|
+
send(data)
|
18
|
+
EM.next_tick(&method(:start))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def connected?
|
23
|
+
!self.channel.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
def send(message)
|
27
|
+
if connected?
|
28
|
+
web_log('client_message', nil, "Message", message)
|
29
|
+
self.channel.push(message)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# types:
|
34
|
+
# 'socket_opened'
|
35
|
+
# 'socket_closed'
|
36
|
+
# 'channel_occupied'
|
37
|
+
# 'channel_vacated'
|
38
|
+
# 'channel_subscribed'
|
39
|
+
# 'channel_unsubscribed'
|
40
|
+
# 'api_message'
|
41
|
+
# 'client_message'
|
42
|
+
# 'presence_message'
|
43
|
+
# 'error'
|
44
|
+
# 'api_error'
|
45
|
+
# 'client_error'
|
46
|
+
# 'pipe_incoming_message'
|
47
|
+
# 'wsapi_message'
|
48
|
+
#
|
49
|
+
def web_log(type, socket_id, message = nil, data = nil)
|
50
|
+
if connected?
|
51
|
+
hash = {
|
52
|
+
:event => 'log_message',
|
53
|
+
:data => {
|
54
|
+
:socket_id => socket_id,
|
55
|
+
:message => message,
|
56
|
+
:date => Time.now.strftime("%H:%M:%S"),
|
57
|
+
:type => type,
|
58
|
+
:data => data,
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
self.channel.push(hash.to_json)
|
63
|
+
end
|
64
|
+
|
65
|
+
Rfid.log(type, socket_id, message, data)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|