nickel-silver-server 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +6 -0
- data/License.txt +3 -0
- data/Manifest.txt +22 -0
- data/README.txt +43 -0
- data/Rakefile +4 -0
- data/config/hoe.rb +70 -0
- data/config/requirements.rb +17 -0
- data/lib/LocoBufferUSB.rb +63 -0
- data/lib/LocoNetServer.rb +247 -0
- data/lib/nickel-silver-server/version.rb +11 -0
- data/lib/nickel-silver-server.rb +13 -0
- data/log/debug.log +0 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/website.rake +17 -0
- data/test/test_helper.rb +2 -0
- data/test/test_nickel-silver-server.rb +11 -0
- metadata +79 -0
data/History.txt
ADDED
data/License.txt
ADDED
data/Manifest.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
History.txt
|
|
2
|
+
License.txt
|
|
3
|
+
Manifest.txt
|
|
4
|
+
README.txt
|
|
5
|
+
Rakefile
|
|
6
|
+
config/hoe.rb
|
|
7
|
+
config/requirements.rb
|
|
8
|
+
lib/nickel-silver-server.rb
|
|
9
|
+
lib/nickel-silver-server/version.rb
|
|
10
|
+
lib/LocoBufferUSB.rb
|
|
11
|
+
lib/LocoNetServer.rb
|
|
12
|
+
log/debug.log
|
|
13
|
+
script/destroy
|
|
14
|
+
script/generate
|
|
15
|
+
script/txt2html
|
|
16
|
+
setup.rb
|
|
17
|
+
tasks/deployment.rake
|
|
18
|
+
tasks/environment.rake
|
|
19
|
+
tasks/website.rake
|
|
20
|
+
test/test_helper.rb
|
|
21
|
+
test/test_nickel-silver-server.rb
|
|
22
|
+
|
data/README.txt
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
= nickel-silver-server
|
|
2
|
+
|
|
3
|
+
* http://rubyforge.org/projects/nickel-silver/
|
|
4
|
+
|
|
5
|
+
== DESCRIPTION:
|
|
6
|
+
|
|
7
|
+
Ruby, literally, on rails! A Ruby implementation of the LocoNetOverTCP protocol allowing remote clients to connect to Digitrax based model railway layouts. Also included are a number of tools for LocoNet logging and locomotive control.
|
|
8
|
+
|
|
9
|
+
== FEATURES/PROBLEMS:
|
|
10
|
+
|
|
11
|
+
* Complete support for LocoNetOverTCP version 1
|
|
12
|
+
|
|
13
|
+
== SYNOPSIS:
|
|
14
|
+
|
|
15
|
+
require 'rubygems'
|
|
16
|
+
require 'nickel-silver-server'
|
|
17
|
+
|
|
18
|
+
# connect to a LocoBufferUSB on the virtual serial port /dev/tty.serialport
|
|
19
|
+
interface = LocoBufferUSB.new( '/dev/tty.serialport' )
|
|
20
|
+
|
|
21
|
+
# create a server using the default port (i.e. 5626, 'loco' spelt on a phone keypad)
|
|
22
|
+
# using our freshly connected LocoBuffer-USB
|
|
23
|
+
server = LocoNetServer.new( interface )
|
|
24
|
+
|
|
25
|
+
# start the server
|
|
26
|
+
server.start
|
|
27
|
+
|
|
28
|
+
# wait for the server to stop before exiting
|
|
29
|
+
server.join
|
|
30
|
+
|
|
31
|
+
== REQUIREMENTS:
|
|
32
|
+
|
|
33
|
+
* ruby-serialport is needed to connect with LocoBuffer-USB hardware http://rubyforge.org/projects/ruby-serialport/
|
|
34
|
+
|
|
35
|
+
== INSTALL:
|
|
36
|
+
|
|
37
|
+
* sudo gem install nickel-silver-server
|
|
38
|
+
|
|
39
|
+
== LICENSE:
|
|
40
|
+
|
|
41
|
+
Nickel Silver is distributed under the same terms as Ruby.
|
|
42
|
+
|
|
43
|
+
Copyright (c) 2008 Tobin Richard
|
data/Rakefile
ADDED
data/config/hoe.rb
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'nickel-silver-server/version'
|
|
2
|
+
|
|
3
|
+
AUTHOR = 'Tobin Richard' # can also be an array of Authors
|
|
4
|
+
EMAIL = "tobin.richard@gmail.com"
|
|
5
|
+
DESCRIPTION = "A Ruby implementation of a LocoNetOverTCP server."
|
|
6
|
+
GEM_NAME = 'nickel-silver-server' # what ppl will type to install your gem
|
|
7
|
+
RUBYFORGE_PROJECT = 'nickel-silver' # The unix name for your project
|
|
8
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
|
9
|
+
DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
|
|
10
|
+
|
|
11
|
+
@config_file = "~/.rubyforge/user-config.yml"
|
|
12
|
+
@config = nil
|
|
13
|
+
RUBYFORGE_USERNAME = "unknown"
|
|
14
|
+
def rubyforge_username
|
|
15
|
+
unless @config
|
|
16
|
+
begin
|
|
17
|
+
@config = YAML.load(File.read(File.expand_path(@config_file)))
|
|
18
|
+
rescue
|
|
19
|
+
puts <<-EOS
|
|
20
|
+
ERROR: No rubyforge config file found: #{@config_file}
|
|
21
|
+
Run 'rubyforge setup' to prepare your env for access to Rubyforge
|
|
22
|
+
- See http://newgem.rubyforge.org/rubyforge.html for more details
|
|
23
|
+
EOS
|
|
24
|
+
exit
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
RUBYFORGE_USERNAME.replace @config["username"]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
REV = nil
|
|
32
|
+
# UNCOMMENT IF REQUIRED:
|
|
33
|
+
# REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
|
|
34
|
+
VERS = NickelSilver::Server::VERSION::STRING + (REV ? ".#{REV}" : "")
|
|
35
|
+
RDOC_OPTS = ['--quiet', '--title', 'nickel-silver-server documentation',
|
|
36
|
+
"--opname", "index.html",
|
|
37
|
+
"--line-numbers",
|
|
38
|
+
"--main", "README",
|
|
39
|
+
"--inline-source"]
|
|
40
|
+
|
|
41
|
+
class Hoe
|
|
42
|
+
def extra_deps
|
|
43
|
+
@extra_deps.reject! { |x| Array(x).first == 'hoe' }
|
|
44
|
+
@extra_deps
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Generate all the Rake tasks
|
|
49
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
|
50
|
+
hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
|
51
|
+
p.developer(AUTHOR, EMAIL)
|
|
52
|
+
p.description = DESCRIPTION
|
|
53
|
+
p.summary = DESCRIPTION
|
|
54
|
+
p.url = HOMEPATH
|
|
55
|
+
p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
|
56
|
+
p.test_globs = ["test/**/test_*.rb"]
|
|
57
|
+
p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
|
|
58
|
+
|
|
59
|
+
# == Optional
|
|
60
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
|
61
|
+
#p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
|
|
62
|
+
|
|
63
|
+
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
|
|
68
|
+
PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
|
|
69
|
+
hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
|
|
70
|
+
hoe.rsync_args = '-av --delete --ignore-errors'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
include FileUtils
|
|
3
|
+
|
|
4
|
+
require 'rubygems'
|
|
5
|
+
%w[rake hoe newgem rubigen].each do |req_gem|
|
|
6
|
+
begin
|
|
7
|
+
require req_gem
|
|
8
|
+
rescue LoadError
|
|
9
|
+
puts "This Rakefile requires the '#{req_gem}' RubyGem."
|
|
10
|
+
puts "Installation: gem install #{req_gem} -y"
|
|
11
|
+
exit
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
$:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
|
|
16
|
+
|
|
17
|
+
require 'nickel-silver-server'
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Explicitly call Kernel#require because the serialport library explodes with rubygem's implementation.
|
|
2
|
+
Kernel.require 'serialport'
|
|
3
|
+
|
|
4
|
+
module NickelSilver
|
|
5
|
+
module Server
|
|
6
|
+
module Interface
|
|
7
|
+
|
|
8
|
+
# A simple IO wrapper for the LocoBuffer-USB.
|
|
9
|
+
#
|
|
10
|
+
# See the documentation for LocoNetServer for details on how this should be used.
|
|
11
|
+
#
|
|
12
|
+
# = Stand-alone usage
|
|
13
|
+
# lb = LocoBufferUSB.new( '/dev/ttys0' )
|
|
14
|
+
#
|
|
15
|
+
# lb.run
|
|
16
|
+
#
|
|
17
|
+
# loop do
|
|
18
|
+
# sleep(1)
|
|
19
|
+
#
|
|
20
|
+
# until lb.input_buffer.empty? do
|
|
21
|
+
# lb.io_mutex.synchronize do
|
|
22
|
+
# puts "Got byte #{ format( '%02x', lb.input_buffer.shift ) } from LocoNet"
|
|
23
|
+
# end
|
|
24
|
+
# end
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
class LocoBufferUSB
|
|
28
|
+
attr_accessor :input_buffer, :output_buffer, :io_mutex
|
|
29
|
+
|
|
30
|
+
# Connect to a LocoBuffer-USB using the specified serial port.
|
|
31
|
+
def initialize( serial_port )
|
|
32
|
+
@locobuffer = SerialPort.new( serial_port, 57_600 )
|
|
33
|
+
|
|
34
|
+
# these may be modified at any time by the server
|
|
35
|
+
@input_buffer = []
|
|
36
|
+
@output_buffer = []
|
|
37
|
+
|
|
38
|
+
# only make changes when locked using @io_mutex
|
|
39
|
+
@io_mutex = @iomutex = Mutex.new
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Handle packets moving in and out of the LocoBuffer-USB.
|
|
43
|
+
def run
|
|
44
|
+
loop do
|
|
45
|
+
while select( [@locobuffer], nil, nil, 0 ) do
|
|
46
|
+
@io_mutex.synchronize do
|
|
47
|
+
@input_buffer << @locobuffer.getc
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# puts "outbuf length = #{output_buffer.length}"
|
|
52
|
+
until output_buffer.empty? do
|
|
53
|
+
@io_mutex.synchronize do
|
|
54
|
+
@locobuffer.putc( @output_buffer.shift )
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
require 'gserver'
|
|
2
|
+
require 'stringio'
|
|
3
|
+
|
|
4
|
+
module NickelSilver
|
|
5
|
+
module Server
|
|
6
|
+
|
|
7
|
+
# = Summary
|
|
8
|
+
# An implementation of the LoconetOverTcp protocol version 1 for use with the
|
|
9
|
+
# LocoBuffer-USB awailable from RR-CirKits (http://www.rr-cirkits.com).
|
|
10
|
+
#
|
|
11
|
+
# This simple protocol allows clients connected via TCP to access a LocoNet netowrk.
|
|
12
|
+
# Both sending and receiving of packets is supported.
|
|
13
|
+
#
|
|
14
|
+
# Author:: Tobin Richard (mailto:tobin.richard@gmail.com)
|
|
15
|
+
# Copyright:: Copyright (c) 2008
|
|
16
|
+
# License:: Distributes under the same terms as Ruby
|
|
17
|
+
#
|
|
18
|
+
# = Usage
|
|
19
|
+
# The following creates a server listening on the default port of 5626 ('loco' spelt on a phone keypad)
|
|
20
|
+
# using a LocoBuffer-USB connected to the serial port <tt>tty.serialport</tt>.
|
|
21
|
+
#
|
|
22
|
+
# require 'rubygems'
|
|
23
|
+
# require 'nickel-silver-server'
|
|
24
|
+
#
|
|
25
|
+
# # connect to a LocoBufferUSB on the virtual serial port /dev/tty.serialport
|
|
26
|
+
# interface = LocoBufferUSB.new( '/dev/tty.serialport' )
|
|
27
|
+
#
|
|
28
|
+
# # create a server using the default port (i.e. 5626, 'loco' spelt on a phone keypad)
|
|
29
|
+
# # using our freshly connected LocoBuffer-USB
|
|
30
|
+
# server = LocoNetServer.new( interface )
|
|
31
|
+
#
|
|
32
|
+
# # start the server
|
|
33
|
+
# server.start
|
|
34
|
+
#
|
|
35
|
+
# # wait for the server to stop before exiting
|
|
36
|
+
# server.join
|
|
37
|
+
#
|
|
38
|
+
# If you want logging of connections, disconnections and other activity then
|
|
39
|
+
# add <tt>server.audit = true</tt> before <tt>server.start</tt>.
|
|
40
|
+
#
|
|
41
|
+
# = Protocol
|
|
42
|
+
# For full details of the LoconetOverTcp protocol see
|
|
43
|
+
# http://loconetovertcp.sourceforge.net/Protocol/LoconetOverTcp.html
|
|
44
|
+
#
|
|
45
|
+
# Information is exchanged between the server and clients as plain ASCII strings. The server
|
|
46
|
+
# ignores invalid commands and empty lines.
|
|
47
|
+
#
|
|
48
|
+
# Clients may send the following commands to the server, as per the protocol specification:
|
|
49
|
+
#
|
|
50
|
+
# SEND Send a packet out over the LocoNet connection. The packet is not checked for correctness
|
|
51
|
+
# before transmission. E.g. <tt>SEND a0 2f 00 70</tt>
|
|
52
|
+
#
|
|
53
|
+
# The server may send the following information to clients, as per the protocol specification:
|
|
54
|
+
#
|
|
55
|
+
# VERSION: Sent to new clients immediately after they connect. The string which follows
|
|
56
|
+
# describes the LocoNetOverTcp server's name and version.
|
|
57
|
+
# E.g. <tt>VERSION NickelSilver version 0.1</tt>
|
|
58
|
+
#
|
|
59
|
+
# RECEIVE: Sent when a packet is received by the LocoBuffer-USB. E.g. <tt>RECEIVE 83 7c</tt>
|
|
60
|
+
#
|
|
61
|
+
# SENT: Sent to clients after an attempt has been made to process a SEND command. First
|
|
62
|
+
# parameter is always <tt>OK</tt> or <tt>ERROR</tt> and may be followed by a string describing
|
|
63
|
+
# details fo the transmission. E.g. <tt>SENT ERROR Could not communicate with LocoBuffer-USB</tt>
|
|
64
|
+
#
|
|
65
|
+
class LocoNetServer < GServer
|
|
66
|
+
|
|
67
|
+
# Creates a new LocoNetOverTCP server.
|
|
68
|
+
#
|
|
69
|
+
# You must supply an interface object and you may specify a port if the default of 5626
|
|
70
|
+
# does not suit your environment.
|
|
71
|
+
#
|
|
72
|
+
# See the full documentation for this class for an exmaple using the LocoBuffer-USB.
|
|
73
|
+
def initialize( interface, tcp_port=5626, *args )
|
|
74
|
+
# we maintain a list of clients to be notified of LocoNet packets
|
|
75
|
+
@clients = []
|
|
76
|
+
|
|
77
|
+
# we will require access to the interface's buffers
|
|
78
|
+
@interface = interface
|
|
79
|
+
|
|
80
|
+
# start the interface buffering in another thread
|
|
81
|
+
Thread.new { @interface.run }
|
|
82
|
+
|
|
83
|
+
# process incoming packets in another thread
|
|
84
|
+
Thread.new { process_packets }
|
|
85
|
+
|
|
86
|
+
super( tcp_port, *args )
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
# Serve a client.
|
|
92
|
+
#
|
|
93
|
+
# The client is registered with the server so it may be notified of LocoNet packets.
|
|
94
|
+
#
|
|
95
|
+
# Only this method may read from clients.
|
|
96
|
+
def serve( io )
|
|
97
|
+
# create a mutex to control access to this clients IO
|
|
98
|
+
semaphore = Mutex.new
|
|
99
|
+
|
|
100
|
+
# store the client and mutex for notification of LocoNet packets
|
|
101
|
+
client = { :io => io, :mutex => semaphore }
|
|
102
|
+
@clients << client
|
|
103
|
+
|
|
104
|
+
# ouput VERSION to client
|
|
105
|
+
semaphore.synchronize do
|
|
106
|
+
io.puts( 'VERSION NickelSilver version 0.1' )
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# read, execute loop
|
|
110
|
+
loop do
|
|
111
|
+
# get any pending input
|
|
112
|
+
# if nil is returned then the client must have disconnected
|
|
113
|
+
line = io.gets
|
|
114
|
+
break if line.nil?
|
|
115
|
+
|
|
116
|
+
command = line.split( ' ' )
|
|
117
|
+
|
|
118
|
+
# LocoNet over TCP Version 1 only supports a single command, SEND
|
|
119
|
+
# ignore all other commands/lines
|
|
120
|
+
if command[0] == 'SEND'
|
|
121
|
+
# convert command into bytes
|
|
122
|
+
packet = command[ 1..command.length ]
|
|
123
|
+
packet.map! { |b| b.to_i(16) }
|
|
124
|
+
|
|
125
|
+
# send packet to loconet
|
|
126
|
+
begin
|
|
127
|
+
send_packet( packet )
|
|
128
|
+
semaphore.synchronize do
|
|
129
|
+
io.puts( 'SENT OK' )
|
|
130
|
+
end
|
|
131
|
+
rescue
|
|
132
|
+
semaphore.synchronize do
|
|
133
|
+
io.puts( 'SENT ERROR' )
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# client has disconnected, remove it from the notification list
|
|
140
|
+
@clients.delete( client )
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Send a packet to the LocoBuffer-USB connection.
|
|
144
|
+
#
|
|
145
|
+
# Does not check the packet format, checksum or anything else.
|
|
146
|
+
#
|
|
147
|
+
# Waits for the the packet sent to be RECEIVE'd back from the LocoBuffer
|
|
148
|
+
def send_packet( packet )
|
|
149
|
+
# create a pipe and mutex and add ourselves as a fake client so we can
|
|
150
|
+
# check the packet is RECEIVE'd back by the LocoBuffer-USB
|
|
151
|
+
reader, writer = IO.pipe
|
|
152
|
+
client = { :io => writer , :mutex => Mutex.new }
|
|
153
|
+
@clients << client
|
|
154
|
+
|
|
155
|
+
# output the bytes
|
|
156
|
+
@interface.io_mutex.synchronize do
|
|
157
|
+
@interface.output_buffer += packet
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# keep waiting for packets either for 2 seconds passes or we get a match
|
|
161
|
+
start = Time.now
|
|
162
|
+
matched = false
|
|
163
|
+
until Time.now - start > 2.0 || matched
|
|
164
|
+
# break it up and remove the leading RECEIVE
|
|
165
|
+
if select( [reader], nil, nil, 0 )
|
|
166
|
+
in_packet = reader.gets().split( ' ' )
|
|
167
|
+
in_packet.delete_at( 0 )
|
|
168
|
+
in_packet.map! { |b| b.to_i(16) }
|
|
169
|
+
|
|
170
|
+
# check for a match
|
|
171
|
+
matched = in_packet == packet
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# remove the fake client from the notification list
|
|
176
|
+
@clients.delete( client )
|
|
177
|
+
|
|
178
|
+
# raise an exception if the packet didn't send
|
|
179
|
+
raise "Did not receive echo from LocoBuffer" unless matched
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Determine if a packet is complete. That is, determine if it has the correct
|
|
183
|
+
# length as determined by its opcode (and possibly its second byte)
|
|
184
|
+
def packet_complete?( packet )
|
|
185
|
+
# if less than two bytes packet can't be finished
|
|
186
|
+
return false if packet.length < 2
|
|
187
|
+
|
|
188
|
+
# Determine correct length. See LocoNet Personal Use Edition 1.0 for
|
|
189
|
+
# information on packet lengths.
|
|
190
|
+
case 0b0110_0000 & packet[0]
|
|
191
|
+
when 0b0000_0000 # two byte packet
|
|
192
|
+
packet.length == 2
|
|
193
|
+
when 0b0010_0000 # four byte packet
|
|
194
|
+
packet.length == 4
|
|
195
|
+
when 0b0100_0000 # six byte packet
|
|
196
|
+
packet.length == 6
|
|
197
|
+
when 0b0110_0000 # lower seven bits of second byte are message length
|
|
198
|
+
packet.length == packet[1]
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
# Process incoming packets and notficy clients.
|
|
204
|
+
def process_packets
|
|
205
|
+
packet = []
|
|
206
|
+
loop do
|
|
207
|
+
# wait for data in the buffer
|
|
208
|
+
# sleep long enough to avoid pegging the CPU
|
|
209
|
+
while @interface.input_buffer.empty?
|
|
210
|
+
sleep(0.1)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# get the next byte out of the buffer
|
|
214
|
+
byte = 0
|
|
215
|
+
@interface.io_mutex.synchronize do
|
|
216
|
+
byte = @interface.input_buffer.shift
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# if this is the first byte it must have its msb set
|
|
220
|
+
packet << byte unless packet.empty? && byte < 0b1000_0000
|
|
221
|
+
|
|
222
|
+
# if we somehow got another opcode before completing a packet
|
|
223
|
+
# then dump the current broken packet and start over
|
|
224
|
+
packet = [byte] if byte >= 0b1000_0000
|
|
225
|
+
|
|
226
|
+
# notify clients if the packet is complete
|
|
227
|
+
if packet_complete?( packet )
|
|
228
|
+
notify_clients( packet )
|
|
229
|
+
|
|
230
|
+
# reset cuurent packet
|
|
231
|
+
packet = []
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Notify all clients of a received packet.
|
|
237
|
+
def notify_clients( packet )
|
|
238
|
+
@clients.each do |client|
|
|
239
|
+
client[:mutex].synchronize do
|
|
240
|
+
client[:io].puts( 'RECEIVE ' + packet.map{ |b| format( '%02x', b ) }.join( ' ' ) )
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
end
|
|
247
|
+
end
|
data/log/debug.log
ADDED
|
File without changes
|
data/script/destroy
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require 'rubigen'
|
|
6
|
+
rescue LoadError
|
|
7
|
+
require 'rubygems'
|
|
8
|
+
require 'rubigen'
|
|
9
|
+
end
|
|
10
|
+
require 'rubigen/scripts/destroy'
|
|
11
|
+
|
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require 'rubigen'
|
|
6
|
+
rescue LoadError
|
|
7
|
+
require 'rubygems'
|
|
8
|
+
require 'rubigen'
|
|
9
|
+
end
|
|
10
|
+
require 'rubigen/scripts/generate'
|
|
11
|
+
|
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
data/script/txt2html
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
begin
|
|
5
|
+
require 'newgem'
|
|
6
|
+
rescue LoadError
|
|
7
|
+
puts "\n\nGenerating the website requires the newgem RubyGem"
|
|
8
|
+
puts "Install: gem install newgem\n\n"
|
|
9
|
+
exit(1)
|
|
10
|
+
end
|
|
11
|
+
require 'redcloth'
|
|
12
|
+
require 'syntax/convertors/html'
|
|
13
|
+
require 'erb'
|
|
14
|
+
require File.dirname(__FILE__) + '/../lib/nickel-silver-server/version.rb'
|
|
15
|
+
|
|
16
|
+
version = Nickel-silver-server::VERSION::STRING
|
|
17
|
+
download = 'http://rubyforge.org/projects/nickel-silver-server'
|
|
18
|
+
|
|
19
|
+
class Fixnum
|
|
20
|
+
def ordinal
|
|
21
|
+
# teens
|
|
22
|
+
return 'th' if (10..19).include?(self % 100)
|
|
23
|
+
# others
|
|
24
|
+
case self % 10
|
|
25
|
+
when 1: return 'st'
|
|
26
|
+
when 2: return 'nd'
|
|
27
|
+
when 3: return 'rd'
|
|
28
|
+
else return 'th'
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class Time
|
|
34
|
+
def pretty
|
|
35
|
+
return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def convert_syntax(syntax, source)
|
|
40
|
+
return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if ARGV.length >= 1
|
|
44
|
+
src, template = ARGV
|
|
45
|
+
template ||= File.join(File.dirname(__FILE__), '/../website/template.rhtml')
|
|
46
|
+
|
|
47
|
+
else
|
|
48
|
+
puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
|
|
49
|
+
exit!
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
template = ERB.new(File.open(template).read)
|
|
53
|
+
|
|
54
|
+
title = nil
|
|
55
|
+
body = nil
|
|
56
|
+
File.open(src) do |fsrc|
|
|
57
|
+
title_text = fsrc.readline
|
|
58
|
+
body_text = fsrc.read
|
|
59
|
+
syntax_items = []
|
|
60
|
+
body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
|
|
61
|
+
ident = syntax_items.length
|
|
62
|
+
element, syntax, source = $1, $2, $3
|
|
63
|
+
syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
|
|
64
|
+
"syntax-temp-#{ident}"
|
|
65
|
+
}
|
|
66
|
+
title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
|
|
67
|
+
body = RedCloth.new(body_text).to_html
|
|
68
|
+
body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
|
|
69
|
+
end
|
|
70
|
+
stat = File.stat(src)
|
|
71
|
+
created = stat.ctime
|
|
72
|
+
modified = stat.mtime
|
|
73
|
+
|
|
74
|
+
$stdout << template.result(binding)
|