live_f1-core 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +12 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +9 -5
- data/README.rdoc +0 -4
- data/Rakefile +3 -0
- data/lib/live_f1/packet/header.rb +4 -1
- data/lib/live_f1/source/live.rb +27 -13
- data/lib/live_f1/source/session.rb +3 -0
- data/lib/live_f1/version.rb +1 -1
- data/live_f1-core.gemspec +5 -8
- metadata +22 -82
- data/bin/live_f1_example +0 -74
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
= LiveF1 Changelog
|
2
|
+
|
3
|
+
== 0.0.2
|
4
|
+
|
5
|
+
* Removed example binary to separate repo at http://github.com/gareth/live_f1-stream
|
6
|
+
* Fixed data logging issue so data will be saved into a replayable file if Source::LiveF1#log_dir is set
|
7
|
+
* Handled error which appeared when streaming in the run-up to a session
|
8
|
+
* Other bugfixes and refactoring
|
9
|
+
|
10
|
+
== 0.0.1
|
11
|
+
|
12
|
+
* Birthday!
|
data/Gemfile
CHANGED
@@ -1,3 +1,15 @@
|
|
1
1
|
source :gemcutter
|
2
2
|
|
3
3
|
gemspec
|
4
|
+
|
5
|
+
# Guard dependencies are here rather than in the gemspec, as gemspec doesn't
|
6
|
+
# offer a way to conditionally require gems per platform, like below
|
7
|
+
#
|
8
|
+
# They aren't development-critical enough to worry about, anyway
|
9
|
+
gem 'guard-rspec'
|
10
|
+
gem 'guard-cucumber'
|
11
|
+
|
12
|
+
# `:require => ...` will evaluate to either `false` or the correct library string depending on the platform
|
13
|
+
gem 'growl', :require => RUBY_PLATFORM.include?('darwin') && 'growl'
|
14
|
+
gem 'rb-fsevent', :require => RUBY_PLATFORM.include?('darwin') && 'rb-fsevent'
|
15
|
+
gem 'rb-inotify', :require => RUBY_PLATFORM.include?('linux') && 'rb-inotify'
|
data/Gemfile.lock
CHANGED
@@ -15,6 +15,7 @@ GEM
|
|
15
15
|
json (>= 1.4.6)
|
16
16
|
diff-lcs (1.1.3)
|
17
17
|
fakeweb (1.3.0)
|
18
|
+
ffi (1.1.5)
|
18
19
|
gherkin (2.11.5)
|
19
20
|
json (>= 1.4.6)
|
20
21
|
growl (1.0.3)
|
@@ -29,7 +30,6 @@ GEM
|
|
29
30
|
guard-rspec (2.1.0)
|
30
31
|
guard (>= 1.1)
|
31
32
|
rspec (~> 2.11)
|
32
|
-
hpricot (0.8.6)
|
33
33
|
json (1.7.5)
|
34
34
|
listen (0.5.3)
|
35
35
|
lumberjack (1.0.2)
|
@@ -38,7 +38,10 @@ GEM
|
|
38
38
|
coderay (~> 1.0.5)
|
39
39
|
method_source (~> 0.8)
|
40
40
|
slop (~> 3.3.1)
|
41
|
+
rake (0.9.2.2)
|
41
42
|
rb-fsevent (0.9.2)
|
43
|
+
rb-inotify (0.8.8)
|
44
|
+
ffi (>= 0.5.0)
|
42
45
|
rspec (2.11.0)
|
43
46
|
rspec-core (~> 2.11.0)
|
44
47
|
rspec-expectations (~> 2.11.0)
|
@@ -54,12 +57,13 @@ PLATFORMS
|
|
54
57
|
ruby
|
55
58
|
|
56
59
|
DEPENDENCIES
|
57
|
-
cucumber
|
58
|
-
fakeweb
|
60
|
+
cucumber (~> 1.0)
|
61
|
+
fakeweb (~> 1.2, >= 1.2.4)
|
59
62
|
growl
|
60
63
|
guard-cucumber
|
61
64
|
guard-rspec
|
62
|
-
hpricot
|
63
65
|
live_f1-core!
|
66
|
+
rake (>= 0.9)
|
64
67
|
rb-fsevent
|
65
|
-
|
68
|
+
rb-inotify
|
69
|
+
rspec (~> 2.7)
|
data/README.rdoc
CHANGED
data/Rakefile
CHANGED
@@ -20,7 +20,7 @@ module LiveF1
|
|
20
20
|
|
21
21
|
def self.from_source source, event_type
|
22
22
|
bytes = source.read_bytes(2)
|
23
|
-
raise "No data from #{source.inspect}" unless bytes.to_s.length == 2
|
23
|
+
raise MissingData, "No data from #{source.inspect}" unless bytes.to_s.length == 2
|
24
24
|
bits = bytes.to_s.reverse.unpack("B*").first
|
25
25
|
_, data, packet_type, car = bits.match(/^(.{7})(.{4})(.{5})$/).to_a.map { |s| s.to_i(2) }
|
26
26
|
|
@@ -122,6 +122,9 @@ module LiveF1
|
|
122
122
|
# An unknown packet is one that we expect (from experience) to appear in the data stream but don't know its purpose
|
123
123
|
class UnknownPacket < RuntimeError
|
124
124
|
end
|
125
|
+
|
126
|
+
class MissingData < EOFError
|
127
|
+
end
|
125
128
|
end
|
126
129
|
end
|
127
130
|
end
|
data/lib/live_f1/source/live.rb
CHANGED
@@ -25,7 +25,6 @@ module LiveF1
|
|
25
25
|
socket.read(num) or raise EOFError
|
26
26
|
end
|
27
27
|
rescue Timeout::Error, Errno::ETIMEDOUT => e
|
28
|
-
log.flush
|
29
28
|
socket.write("\n")
|
30
29
|
socket.flush
|
31
30
|
retry
|
@@ -35,6 +34,7 @@ module LiveF1
|
|
35
34
|
def keyframe number = nil
|
36
35
|
io = open("http://#{HOST}/#{keyframe_filename(number)}")
|
37
36
|
log.keyframe(number, io.read) if number
|
37
|
+
log.flush
|
38
38
|
io.rewind
|
39
39
|
Source::Keyframe.new io, self
|
40
40
|
rescue SocketError
|
@@ -42,6 +42,7 @@ module LiveF1
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def decryption_key session_number
|
45
|
+
return if session_number.zero?
|
45
46
|
key = open("http://#{HOST}/reg/getkey/#{session_number}.asp?auth=#{auth}").read.to_i(16)
|
46
47
|
raise ConnectionError, "Unable to access session key for session #{session_number}. This could indicate incorrect credentials or an issue with the formula1.com key server" if key.zero?
|
47
48
|
key
|
@@ -67,10 +68,10 @@ module LiveF1
|
|
67
68
|
yield packet
|
68
69
|
end
|
69
70
|
rescue Errno::ECONNRESET
|
70
|
-
log.
|
71
|
+
log.reset
|
71
72
|
retry
|
72
73
|
rescue Exception
|
73
|
-
log.
|
74
|
+
log.reset
|
74
75
|
raise
|
75
76
|
end
|
76
77
|
|
@@ -96,11 +97,13 @@ module LiveF1
|
|
96
97
|
"keyframe#{ "_%05d" % number if number}.bin"
|
97
98
|
end
|
98
99
|
|
99
|
-
|
100
|
+
# Wraps a logfile in methods which mean the caller doesn't need to know
|
101
|
+
# if a log is currently open or not
|
102
|
+
class LogProxy # :nodoc:
|
100
103
|
class << self
|
101
104
|
def start session_number
|
102
|
-
|
103
|
-
@log = Log.new session_number
|
105
|
+
reset
|
106
|
+
@log = Log.new session_number unless session_number.zero?
|
104
107
|
end
|
105
108
|
|
106
109
|
[:key, :packet, :keyframe].each do |m|
|
@@ -109,8 +112,14 @@ module LiveF1
|
|
109
112
|
end
|
110
113
|
end
|
111
114
|
|
115
|
+
# Writes the current logfile to disk
|
112
116
|
def flush
|
113
117
|
@log.flush if @log
|
118
|
+
end
|
119
|
+
|
120
|
+
# Writes the current logfile to disk and then closes the log
|
121
|
+
def reset
|
122
|
+
flush
|
114
123
|
@log = nil
|
115
124
|
end
|
116
125
|
end
|
@@ -118,13 +127,14 @@ module LiveF1
|
|
118
127
|
|
119
128
|
class Log
|
120
129
|
class << self
|
121
|
-
|
130
|
+
attr_accessor :dir
|
122
131
|
|
123
132
|
def dir= log_directory
|
124
133
|
@dir = Pathname.new(log_directory).join(Date.today.strftime("%Y%m%d"))
|
125
134
|
FileUtils.mkdir_p(@dir)
|
126
135
|
end
|
127
136
|
|
137
|
+
# Has logging been set up (do we have a log directory to write to)?
|
128
138
|
def active?
|
129
139
|
!!@dir
|
130
140
|
end
|
@@ -139,18 +149,22 @@ module LiveF1
|
|
139
149
|
}
|
140
150
|
end
|
141
151
|
|
142
|
-
|
143
|
-
|
152
|
+
# Adds a decryption key to this log
|
153
|
+
def key key
|
154
|
+
@data[:key] = key
|
144
155
|
end
|
145
156
|
|
146
|
-
|
147
|
-
|
157
|
+
# Adds a packet to this log's bytestream
|
158
|
+
def packet packet
|
159
|
+
@data[:bytes] << packet.header.bytes << packet.bytes
|
148
160
|
end
|
149
161
|
|
150
|
-
|
151
|
-
|
162
|
+
# Adds keyframe `n` to this log
|
163
|
+
def keyframe number, keyframe_bytes
|
164
|
+
@data[:keyframes][number] = keyframe_bytes
|
152
165
|
end
|
153
166
|
|
167
|
+
# Writes this logfile to disk
|
154
168
|
def flush
|
155
169
|
File.open(@filename, "w") { |f| f.write YAML.dump(@data) } if @filename
|
156
170
|
end
|
@@ -27,6 +27,9 @@ module LiveF1
|
|
27
27
|
# Decrypts the given string using this session's decryption_key and the
|
28
28
|
# current state of the decryption_salt.
|
29
29
|
def decrypt input
|
30
|
+
# Sometimes we don't have a decryption key, e.g. Notice is a
|
31
|
+
# decryptable packet but sometimes appears between sessions
|
32
|
+
return input unless decryption_key
|
30
33
|
input.bytes.map do |b|
|
31
34
|
self.decryption_salt = (decryption_salt >> 1) ^ ((decryption_salt & 0x01).zero? ? 0 : decryption_key)
|
32
35
|
b ^ (decryption_salt & 0xff)
|
data/lib/live_f1/version.rb
CHANGED
data/live_f1-core.gemspec
CHANGED
@@ -15,12 +15,9 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = LiveF1::VERSION
|
17
17
|
|
18
|
-
gem.add_development_dependency
|
19
|
-
gem.add_development_dependency
|
20
|
-
|
21
|
-
gem.add_development_dependency
|
22
|
-
gem.add_development_dependency
|
23
|
-
gem.add_development_dependency "growl"
|
24
|
-
gem.add_development_dependency "fakeweb"
|
25
|
-
gem.add_development_dependency "rb-fsevent"
|
18
|
+
gem.add_development_dependency 'rspec', '~> 2.7'
|
19
|
+
gem.add_development_dependency 'cucumber', '~> 1.0'
|
20
|
+
|
21
|
+
gem.add_development_dependency 'rake', '>= 0.9'
|
22
|
+
gem.add_development_dependency 'fakeweb', '~> 1.2', '>= 1.2.4'
|
26
23
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: live_f1-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,96 +9,48 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-11-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: hpricot
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ! '>='
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '0'
|
22
|
-
type: :development
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ! '>='
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '0'
|
30
14
|
- !ruby/object:Gem::Dependency
|
31
15
|
name: rspec
|
32
16
|
requirement: !ruby/object:Gem::Requirement
|
33
17
|
none: false
|
34
18
|
requirements:
|
35
|
-
- -
|
19
|
+
- - ~>
|
36
20
|
- !ruby/object:Gem::Version
|
37
|
-
version: '
|
21
|
+
version: '2.7'
|
38
22
|
type: :development
|
39
23
|
prerelease: false
|
40
24
|
version_requirements: !ruby/object:Gem::Requirement
|
41
25
|
none: false
|
42
26
|
requirements:
|
43
|
-
- -
|
27
|
+
- - ~>
|
44
28
|
- !ruby/object:Gem::Version
|
45
|
-
version: '
|
29
|
+
version: '2.7'
|
46
30
|
- !ruby/object:Gem::Dependency
|
47
31
|
name: cucumber
|
48
32
|
requirement: !ruby/object:Gem::Requirement
|
49
33
|
none: false
|
50
34
|
requirements:
|
51
|
-
- -
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version: '0'
|
54
|
-
type: :development
|
55
|
-
prerelease: false
|
56
|
-
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
|
-
requirements:
|
59
|
-
- - ! '>='
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
- !ruby/object:Gem::Dependency
|
63
|
-
name: guard-rspec
|
64
|
-
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
|
-
requirements:
|
67
|
-
- - ! '>='
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: '0'
|
70
|
-
type: :development
|
71
|
-
prerelease: false
|
72
|
-
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
|
-
requirements:
|
75
|
-
- - ! '>='
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version: '0'
|
78
|
-
- !ruby/object:Gem::Dependency
|
79
|
-
name: guard-cucumber
|
80
|
-
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
|
-
requirements:
|
83
|
-
- - ! '>='
|
35
|
+
- - ~>
|
84
36
|
- !ruby/object:Gem::Version
|
85
|
-
version: '0'
|
37
|
+
version: '1.0'
|
86
38
|
type: :development
|
87
39
|
prerelease: false
|
88
40
|
version_requirements: !ruby/object:Gem::Requirement
|
89
41
|
none: false
|
90
42
|
requirements:
|
91
|
-
- -
|
43
|
+
- - ~>
|
92
44
|
- !ruby/object:Gem::Version
|
93
|
-
version: '0'
|
45
|
+
version: '1.0'
|
94
46
|
- !ruby/object:Gem::Dependency
|
95
|
-
name:
|
47
|
+
name: rake
|
96
48
|
requirement: !ruby/object:Gem::Requirement
|
97
49
|
none: false
|
98
50
|
requirements:
|
99
51
|
- - ! '>='
|
100
52
|
- !ruby/object:Gem::Version
|
101
|
-
version: '0'
|
53
|
+
version: '0.9'
|
102
54
|
type: :development
|
103
55
|
prerelease: false
|
104
56
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -106,56 +58,45 @@ dependencies:
|
|
106
58
|
requirements:
|
107
59
|
- - ! '>='
|
108
60
|
- !ruby/object:Gem::Version
|
109
|
-
version: '0'
|
61
|
+
version: '0.9'
|
110
62
|
- !ruby/object:Gem::Dependency
|
111
63
|
name: fakeweb
|
112
64
|
requirement: !ruby/object:Gem::Requirement
|
113
65
|
none: false
|
114
66
|
requirements:
|
115
|
-
- -
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
|
-
requirements:
|
123
|
-
- - ! '>='
|
67
|
+
- - ~>
|
124
68
|
- !ruby/object:Gem::Version
|
125
|
-
version: '
|
126
|
-
- !ruby/object:Gem::Dependency
|
127
|
-
name: rb-fsevent
|
128
|
-
requirement: !ruby/object:Gem::Requirement
|
129
|
-
none: false
|
130
|
-
requirements:
|
69
|
+
version: '1.2'
|
131
70
|
- - ! '>='
|
132
71
|
- !ruby/object:Gem::Version
|
133
|
-
version:
|
72
|
+
version: 1.2.4
|
134
73
|
type: :development
|
135
74
|
prerelease: false
|
136
75
|
version_requirements: !ruby/object:Gem::Requirement
|
137
76
|
none: false
|
138
77
|
requirements:
|
78
|
+
- - ~>
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '1.2'
|
139
81
|
- - ! '>='
|
140
82
|
- !ruby/object:Gem::Version
|
141
|
-
version:
|
83
|
+
version: 1.2.4
|
142
84
|
description: Parses raw events from the Formula1.com live timing stream
|
143
85
|
email:
|
144
86
|
- g@rethada.ms
|
145
|
-
executables:
|
146
|
-
- live_f1_example
|
87
|
+
executables: []
|
147
88
|
extensions: []
|
148
89
|
extra_rdoc_files: []
|
149
90
|
files:
|
150
91
|
- .autotest
|
151
92
|
- .gitignore
|
152
93
|
- .rspec
|
94
|
+
- CHANGELOG.rdoc
|
153
95
|
- Gemfile
|
154
96
|
- Gemfile.lock
|
155
97
|
- Guardfile
|
156
98
|
- README.rdoc
|
157
99
|
- Rakefile
|
158
|
-
- bin/live_f1_example
|
159
100
|
- features/fixtures/sessions/2012.03.china.qualifying/7136/keyframes.yaml
|
160
101
|
- features/fixtures/sessions/2012.03.china.qualifying/7136/session.key
|
161
102
|
- features/fixtures/sessions/2012.03.china.qualifying/session.bin
|
@@ -334,4 +275,3 @@ test_files:
|
|
334
275
|
- spec/live_f1/source_spec.rb
|
335
276
|
- spec/spec_helper.rb
|
336
277
|
- spec/support/packet_type_examples.rb
|
337
|
-
has_rdoc:
|
data/bin/live_f1_example
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
require 'rubygems'
|
3
|
-
require 'optparse'
|
4
|
-
require 'highline'
|
5
|
-
|
6
|
-
$: << File.join(File.dirname(__FILE__), '..', 'lib')
|
7
|
-
require 'live_f1'
|
8
|
-
|
9
|
-
options = {}
|
10
|
-
OptionParser.new do |opts|
|
11
|
-
opts.separator ""
|
12
|
-
opts.separator "Specific options:"
|
13
|
-
|
14
|
-
opts.on("-f LOGFILE", "--file", "Replays the given previously recorded .f1 data file (if not specified, will attempt to connect to the live timing server)") do |logfile|
|
15
|
-
options[:logfile] = logfile
|
16
|
-
end
|
17
|
-
opts.on("-u USERNAME", "--username", "For live connections, specify the formula1.com live timing username") do |username|
|
18
|
-
options[:username] = username
|
19
|
-
end
|
20
|
-
opts.on("-p [PASSWORD]", "--password", "For live connections, specify the formula1.com live timing password. Omitting the password will cause live-f1 to prompt for it.") do |password|
|
21
|
-
unless password
|
22
|
-
prompt = HighLine.new
|
23
|
-
password = prompt.ask("Password: ") { |q| q.echo = false }
|
24
|
-
end
|
25
|
-
options[:password] = password
|
26
|
-
end
|
27
|
-
opts.on("-d", "--debug", "Display bit data from each data packet's header") do
|
28
|
-
LiveF1.debug = true
|
29
|
-
end
|
30
|
-
|
31
|
-
opts.on_tail("-h", "--help", "Show this message") do
|
32
|
-
puts opts
|
33
|
-
exit
|
34
|
-
end
|
35
|
-
|
36
|
-
end.parse!
|
37
|
-
|
38
|
-
if options[:logfile]
|
39
|
-
|
40
|
-
timestamp = nil
|
41
|
-
packets = []
|
42
|
-
|
43
|
-
source = LiveF1::Source::Log.new(File.open(options[:logfile]))
|
44
|
-
source.run do |packet|
|
45
|
-
packets << packet
|
46
|
-
case packet
|
47
|
-
when LiveF1::Packet::Sys::Timestamp
|
48
|
-
t_start = timestamp || packet.number
|
49
|
-
t_end = packet.number
|
50
|
-
t_diff = t_end - t_start
|
51
|
-
|
52
|
-
# The interval is the difference in time between the last 2 timestamps, divided
|
53
|
-
# by the number of packets between the timestamps. This is then divided by 4 to
|
54
|
-
# speed up the replay
|
55
|
-
interval = t_diff.to_f / (packets.length) / 8
|
56
|
-
packets.pop # Remove the timestamp packet from the output queue
|
57
|
-
while p = packets.shift
|
58
|
-
puts p.inspect
|
59
|
-
sleep interval
|
60
|
-
end
|
61
|
-
timestamp = packet.number
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
else
|
66
|
-
|
67
|
-
source = LiveF1::Source::Live.new(options[:username], options[:password])
|
68
|
-
source.log_dir = "./data"
|
69
|
-
source.run do |packet|
|
70
|
-
puts packet.inspect
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
|