live_f1-core 0.0.1 → 0.0.2
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/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
|
-
|