multi_movingsign 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/.gitignore +21 -0
- data/.rspec +2 -0
- data/.travis.yml +9 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/PAGE_DEFINITION.md +115 -0
- data/README.md +133 -0
- data/Rakefile +1 -0
- data/bin/multi_movingsign +5 -0
- data/example.jpg +0 -0
- data/fonts/7-row-normal.png +0 -0
- data/fonts/README.md +1 -0
- data/lib/multi_movingsign.rb +9 -0
- data/lib/multi_movingsign/cli.rb +81 -0
- data/lib/multi_movingsign/errors.rb +10 -0
- data/lib/multi_movingsign/page_renderer.rb +223 -0
- data/lib/multi_movingsign/server.rb +317 -0
- data/lib/multi_movingsign/settings.rb +55 -0
- data/lib/multi_movingsign/sign.rb +37 -0
- data/lib/multi_movingsign/signs.rb +39 -0
- data/lib/multi_movingsign/testrc_loader.rb +17 -0
- data/lib/multi_movingsign/version.rb +3 -0
- data/multi_movingsign.gemspec +31 -0
- data/spec/cli_spec.rb +166 -0
- data/spec/noop_movingsign_sign.rb +47 -0
- data/spec/noop_movingsign_sign.yml +7 -0
- data/spec/page_renderer/example_1/1.yml +16 -0
- data/spec/page_renderer/example_1/2.yml +28 -0
- data/spec/page_renderer/example_1/4.yml +44 -0
- data/spec/page_renderer/example_1/5.json +10 -0
- data/spec/page_renderer/example_1/example_spec.rb +23 -0
- data/spec/page_renderer/example_1/page.yml +27 -0
- data/spec/page_renderer/example_2/1.yml +7 -0
- data/spec/page_renderer/example_2/2.yml +8 -0
- data/spec/page_renderer/example_2/4.json +9 -0
- data/spec/page_renderer/example_2/example_spec.rb +24 -0
- data/spec/page_renderer/example_2/page.yml +7 -0
- data/spec/page_renderer/example_3/1.yml +9 -0
- data/spec/page_renderer/example_3/3.yml +14 -0
- data/spec/page_renderer/example_3/example_spec.rb +22 -0
- data/spec/page_renderer/example_3/page.yml +12 -0
- data/spec/page_renderer/example_4/2.yml +12 -0
- data/spec/page_renderer/example_4/4.json +9 -0
- data/spec/page_renderer/example_4/example_spec.rb +24 -0
- data/spec/page_renderer/example_4/page.yml +12 -0
- data/spec/settings_1.yml +3 -0
- data/spec/settings_spec.rb +36 -0
- data/spec/spec_helper.rb +36 -0
- data/spec/support/doubles_support.rb +26 -0
- data/spec/support/executable_support.rb +112 -0
- metadata +244 -0
@@ -0,0 +1,223 @@
|
|
1
|
+
module MultiMovingsign
|
2
|
+
# Renders a page definition (Hash/YAML) into something that is easily displayable by the MovingsignApi (page solution)
|
3
|
+
class PageRenderer
|
4
|
+
DEFAULT_CHARACTER_WIDTH = 5
|
5
|
+
|
6
|
+
# @param page [Hash] page definition as a Hash
|
7
|
+
# @param options [Hash] options for the rendering operation
|
8
|
+
# @option options [Integer] :count the number of signs to render to (default: 1)
|
9
|
+
#
|
10
|
+
# @return [Hash] a page solution hash
|
11
|
+
def render(page, options = {})
|
12
|
+
# Vocabulary - Terms used here
|
13
|
+
#
|
14
|
+
# Sign
|
15
|
+
# A single LED sign, stacked vertically with other LED signs...together forming a screen
|
16
|
+
# Screen
|
17
|
+
# N LED signs stacked vertically. Together they can display a screen of information at a time
|
18
|
+
# Page Definition
|
19
|
+
# A page of information to be broken up and displayed on available signs, consisting of a title and n Line Definitions.
|
20
|
+
# Line Definition
|
21
|
+
# A line of information from the page definition. NOTE: a single line might turn into multiple screens of information
|
22
|
+
# Line Segment
|
23
|
+
# A piece of a line definition, displayed on it's own sign, seprate from previous line segments of the same line.
|
24
|
+
#
|
25
|
+
|
26
|
+
signs_available = (options[:count] || 1)
|
27
|
+
page_title = page['title']
|
28
|
+
line_definitions = page['lines']
|
29
|
+
pin_title = signs_available > 1
|
30
|
+
|
31
|
+
page_definition = PageDefinition.from_hash page
|
32
|
+
page_segments = page_definition.calculate_segments(signs_available)
|
33
|
+
screens = page_segments.map { |s| s.calculate_screens(signs_available, screen_width) }.flatten
|
34
|
+
|
35
|
+
## Preview Solution
|
36
|
+
#screens.each do |screen|
|
37
|
+
# puts "----"
|
38
|
+
# (0..(signs_available-1)).each do |i|
|
39
|
+
# puts screen.line(i)
|
40
|
+
# end
|
41
|
+
#end
|
42
|
+
#puts "----"
|
43
|
+
|
44
|
+
signs = (0..(signs_available-1)).map { |sign_index| {'content' => screens.map { |s| s.line(sign_index) }.join("\n")} }
|
45
|
+
|
46
|
+
{'signs' => signs, 'lines' => screens.length}
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def screen_width
|
52
|
+
80
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.calculate_width(string)
|
56
|
+
string.length * DEFAULT_CHARACTER_WIDTH
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class PageDefinition
|
61
|
+
attr_accessor :title
|
62
|
+
attr_accessor :line_definitions
|
63
|
+
|
64
|
+
def self.from_hash(hash)
|
65
|
+
obj = self.new
|
66
|
+
|
67
|
+
obj.title = hash['title'] || ''
|
68
|
+
obj.line_definitions = hash['lines'].map { |ld| LineDefinition.from_hash ld }
|
69
|
+
|
70
|
+
obj
|
71
|
+
end
|
72
|
+
|
73
|
+
# Splits a {PageDefinition} into an array of {PageSegment}s
|
74
|
+
#
|
75
|
+
# @param signs [Integer] the number of signs (lines) available to render to
|
76
|
+
# @param options [Hash]
|
77
|
+
# @option options [Boolean] +:pin_title+
|
78
|
+
def calculate_segments(signs, options = {})
|
79
|
+
pin_title = signs > 1 && (options[:pin_title] != false)
|
80
|
+
page_segments = []
|
81
|
+
line_definitions = self.line_definitions.clone.reverse
|
82
|
+
|
83
|
+
index = 0
|
84
|
+
while !line_definitions.empty?
|
85
|
+
include_title = pin_title || index == 0 # include the title in this line segment?
|
86
|
+
line_count = include_title ? signs - 1 : signs # number of line definitions to include in this page segment (less the title if included)
|
87
|
+
|
88
|
+
page_segments << PageSegment.new(include_title ? self.title : nil, line_definitions.pop(line_count).reverse)
|
89
|
+
|
90
|
+
index += 1
|
91
|
+
end
|
92
|
+
|
93
|
+
page_segments
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class LineDefinition
|
98
|
+
attr_accessor :prefix
|
99
|
+
attr_accessor :line_segments
|
100
|
+
|
101
|
+
def self.from_hash(hash)
|
102
|
+
obj = self.new
|
103
|
+
|
104
|
+
obj.prefix = hash['prefix'] || nil
|
105
|
+
obj.line_segments = (hash['segments'] || hash['content'] || []).map { |segment| LineSegment.new(obj.prefix, segment) }
|
106
|
+
|
107
|
+
obj
|
108
|
+
end
|
109
|
+
|
110
|
+
def prefix?
|
111
|
+
!!self.prefix
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class LineSegment
|
116
|
+
attr_accessor :prefix
|
117
|
+
attr_accessor :segment
|
118
|
+
|
119
|
+
def initialize(prefix, segment)
|
120
|
+
self.prefix = prefix
|
121
|
+
self.segment = segment
|
122
|
+
end
|
123
|
+
|
124
|
+
def prefix?
|
125
|
+
!!self.prefix
|
126
|
+
end
|
127
|
+
|
128
|
+
# If necessary, splits this LineSegment into multiple appropriate for displaying at once on the screen
|
129
|
+
def split_if_necessary(max_width)
|
130
|
+
raise InvalidInputError, "Prefix '' is too wide!" if (prefix? && PageRenderer.calculate_width(prefix) > max_width)
|
131
|
+
|
132
|
+
if PageRenderer.calculate_width(self.to_s) <= max_width
|
133
|
+
# segment isn't too long with prefix, return it as is
|
134
|
+
[self]
|
135
|
+
else
|
136
|
+
# segment is too long, split it up into word segments finding the largest with the prefix appended that fits
|
137
|
+
segments = [] # calculated segments
|
138
|
+
prefix_width = prefix? ? PageRenderer.calculate_width(prefix) : 0
|
139
|
+
|
140
|
+
words = segment.split(/ /)
|
141
|
+
while !words.empty?
|
142
|
+
index = words.length
|
143
|
+
while index > 0 && PageRenderer.calculate_width(candidate = (candidate_words = words[0, index]).join(' ')) + prefix_width > max_width
|
144
|
+
index -= 1
|
145
|
+
end
|
146
|
+
|
147
|
+
segments << self.class.new(prefix, candidate)
|
148
|
+
words = words.drop index
|
149
|
+
end
|
150
|
+
|
151
|
+
segments
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def to_s
|
156
|
+
prefix? ? prefix + segment : segment
|
157
|
+
end
|
158
|
+
|
159
|
+
def inspect
|
160
|
+
to_s.inspect
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class PageSegment
|
165
|
+
attr_accessor :title
|
166
|
+
attr_accessor :line_definitions
|
167
|
+
|
168
|
+
def initialize(title, line_definitions)
|
169
|
+
self.title = title
|
170
|
+
self.line_definitions = line_definitions
|
171
|
+
end
|
172
|
+
|
173
|
+
def title?
|
174
|
+
!!self.title
|
175
|
+
end
|
176
|
+
|
177
|
+
def line_definitions?
|
178
|
+
!self.line_definitions.empty?
|
179
|
+
end
|
180
|
+
|
181
|
+
# Turns a single page segment into n rendered screens of information
|
182
|
+
def calculate_screens(number_of_signs, sign_width)
|
183
|
+
raise "Title too long!" if title? && PageRenderer.calculate_width(title) > sign_width
|
184
|
+
|
185
|
+
if title? && !line_definitions?
|
186
|
+
return Screen.new [title]
|
187
|
+
end
|
188
|
+
|
189
|
+
screens = []
|
190
|
+
|
191
|
+
#puts line_definitions.map { |d| d.line_segments.map { |s| s.split_if_necessary(sign_width).map { |s| s.to_s} } }.inspect
|
192
|
+
|
193
|
+
num_of_line_segments = line_definitions.map { |d| d.line_segments.length }.max
|
194
|
+
(0..(num_of_line_segments - 1)).each do |segment_index|
|
195
|
+
subsegments = line_definitions.map { |d| (s = d.line_segments[segment_index]) ? s.split_if_necessary(sign_width) : [] }
|
196
|
+
num_of_subsegments = subsegments.map { |s| s.length }.max
|
197
|
+
|
198
|
+
(0..(num_of_subsegments-1)).each do |subsegment_index|
|
199
|
+
lines = []
|
200
|
+
|
201
|
+
lines << title if title?
|
202
|
+
lines.concat subsegments.map { |s| s[subsegment_index % s.length] || "" }
|
203
|
+
|
204
|
+
screens << Screen.new(lines)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
screens
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
class Screen
|
213
|
+
attr_accessor :lines
|
214
|
+
|
215
|
+
def initialize(lines)
|
216
|
+
self.lines = lines
|
217
|
+
end
|
218
|
+
|
219
|
+
def line(index)
|
220
|
+
self.lines[index] || " "
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,317 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'thread'
|
3
|
+
require 'socket'
|
4
|
+
require 'multi_movingsign/signs'
|
5
|
+
require 'multi_movingsign/testrc_loader'
|
6
|
+
|
7
|
+
module MultiMovingsign
|
8
|
+
# http://stackoverflow.com/a/9439298
|
9
|
+
class TeeIO < IO
|
10
|
+
attr_accessor :destinations
|
11
|
+
|
12
|
+
def initialize(*dests)
|
13
|
+
self.destinations = dests
|
14
|
+
end
|
15
|
+
|
16
|
+
def puts(val)
|
17
|
+
time = Time.now
|
18
|
+
destinations.each do |d|
|
19
|
+
d.puts "#{time}: #{val.to_s}"
|
20
|
+
d.flush
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def write(val)
|
25
|
+
destinations.each do |d|
|
26
|
+
d.write val
|
27
|
+
d.flush
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# MultiMovingsign server command line interface
|
33
|
+
class Server < Thor
|
34
|
+
class_option :serverrc, :desc => 'Path to server persistent storage. Defaults to ~/.multi_movingsign/server'
|
35
|
+
desc 'start', 'Starts the MutliMovingsign server'
|
36
|
+
def start
|
37
|
+
TestRCLoader.load(options['testrc']) if options['testrc']
|
38
|
+
|
39
|
+
# This impl is a hacky mess... FYI!
|
40
|
+
FileUtils.mkdir_p server_settings_path
|
41
|
+
|
42
|
+
lock_path = File.join(server_settings_path, "server.lock")
|
43
|
+
File.open(lock_path, 'w') do |lock|
|
44
|
+
raise "Cannot acquire lock! Is a server already running?" unless lock.flock(File::LOCK_EX | File::LOCK_NB)
|
45
|
+
|
46
|
+
lock.puts $$
|
47
|
+
lock.flush
|
48
|
+
|
49
|
+
mutex = Mutex.new
|
50
|
+
|
51
|
+
# setup logging
|
52
|
+
log_path = File.join(server_settings_path, "server.log")
|
53
|
+
log = File.new(log_path, "a")
|
54
|
+
$stdout = TeeIO.new($stdout, log)
|
55
|
+
$stderr = TeeIO.new($stderr, log)
|
56
|
+
|
57
|
+
signs = []
|
58
|
+
|
59
|
+
page_keys = []
|
60
|
+
page_solutions = {}
|
61
|
+
page_index = 0
|
62
|
+
alert = nil
|
63
|
+
stop = nil
|
64
|
+
|
65
|
+
Thread.new do
|
66
|
+
begin
|
67
|
+
Socket.unix_server_loop(server_socket_path) do |socket, address|
|
68
|
+
puts "SOCKET LOOP!"
|
69
|
+
|
70
|
+
begin
|
71
|
+
msg = nil
|
72
|
+
|
73
|
+
begin
|
74
|
+
msg, = socket.recvmsg_nonblock
|
75
|
+
rescue IO::WaitReadable
|
76
|
+
if IO.select([socket], [], [], 5)
|
77
|
+
retry
|
78
|
+
else
|
79
|
+
raise TimeoutError, "Timeout in recvmsg_nonblock"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
unless msg
|
84
|
+
$stderr.puts "Bogus unix_server_loop?"
|
85
|
+
next
|
86
|
+
end
|
87
|
+
|
88
|
+
lines = msg.lines.map { |l| l.rstrip }
|
89
|
+
puts "Got UNIX message: #{lines.inspect}"
|
90
|
+
|
91
|
+
version = lines.delete_at 0
|
92
|
+
|
93
|
+
case command = lines.delete_at(0)
|
94
|
+
when 'add page'
|
95
|
+
name = lines.delete_at(0)
|
96
|
+
yaml = lines.join "\n"
|
97
|
+
|
98
|
+
solution = PageRenderer.new.render YAML.load(yaml), :count => signs.length
|
99
|
+
page_path = File.join(server_pages_path, "#{name}.yml")
|
100
|
+
File.open(page_path, "w") { |f| f.puts yaml }
|
101
|
+
|
102
|
+
mutex.synchronize do
|
103
|
+
page_solutions[name] = solution
|
104
|
+
page_keys << name unless page_keys.include? name
|
105
|
+
|
106
|
+
puts "Added #{name}!"
|
107
|
+
|
108
|
+
page_keys.delete 'nada'
|
109
|
+
page_solutions.delete 'nada'
|
110
|
+
end
|
111
|
+
|
112
|
+
socket.puts "okay"
|
113
|
+
when 'delete page'
|
114
|
+
name = lines.delete_at(0)
|
115
|
+
|
116
|
+
mutex.synchronize do
|
117
|
+
page_path = File.join(server_pages_path, "#{name}.yml")
|
118
|
+
|
119
|
+
FileUtils.rm(page_path, :force => true) if File.exists? page_path
|
120
|
+
|
121
|
+
page_keys.delete name
|
122
|
+
page_solutions.delete name
|
123
|
+
end
|
124
|
+
|
125
|
+
puts "Deleted #{name}"
|
126
|
+
|
127
|
+
socket.puts "okay"
|
128
|
+
when 'alert'
|
129
|
+
page_yaml = lines.join("\n")
|
130
|
+
|
131
|
+
mutex.synchronize do
|
132
|
+
condition_variable = ConditionVariable.new
|
133
|
+
alert = {"solution" => PageRenderer.new.render(YAML.load(page_yaml), :count => signs.length), 'condition_variable' => condition_variable}
|
134
|
+
|
135
|
+
puts "Signaling alert..."
|
136
|
+
condition_variable.wait mutex
|
137
|
+
end
|
138
|
+
|
139
|
+
socket.puts "okay"
|
140
|
+
|
141
|
+
when 'stop'
|
142
|
+
mutex.synchronize do
|
143
|
+
cv = ConditionVariable.new
|
144
|
+
|
145
|
+
stop = {'condition_variable' => cv}
|
146
|
+
|
147
|
+
cv.wait mutex
|
148
|
+
end
|
149
|
+
|
150
|
+
socket.puts "okay"
|
151
|
+
else
|
152
|
+
$stderr.puts "Unknown command '#{command}'"
|
153
|
+
end
|
154
|
+
rescue => e
|
155
|
+
$stderr.puts "Exception in unix server loop"
|
156
|
+
$stderr.puts e.message
|
157
|
+
$stderr.puts e.backtrace.join "\n"
|
158
|
+
ensure
|
159
|
+
socket.close
|
160
|
+
puts "SOCKET CLOSED"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
rescue => e
|
164
|
+
$stderr.puts "UNIX socket loop raised!"
|
165
|
+
$stderr.puts e.message
|
166
|
+
$stderr.puts e.backtrace.join '\n'
|
167
|
+
|
168
|
+
Thread::current.pi
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Loop to allow reloaded
|
173
|
+
loop do
|
174
|
+
if stop
|
175
|
+
puts "Outter loop stopping..."
|
176
|
+
break
|
177
|
+
end
|
178
|
+
|
179
|
+
puts "Starting/Reloading!"
|
180
|
+
|
181
|
+
# load sign configuration
|
182
|
+
settings = Settings.load settings_path
|
183
|
+
raise_no_signs unless settings.signs?
|
184
|
+
|
185
|
+
mutex.synchronize do
|
186
|
+
page_keys = []
|
187
|
+
page_solutions = {}
|
188
|
+
signs = Signs.new settings.signs
|
189
|
+
|
190
|
+
# Load pages and solutions
|
191
|
+
FileUtils.mkdir_p server_pages_path
|
192
|
+
Dir.glob(File.join(server_pages_path, '*.yml')).sort.each do |path|
|
193
|
+
puts "Loading #{path}"
|
194
|
+
|
195
|
+
key = File.basename(path, File.extname(path))
|
196
|
+
|
197
|
+
page_keys << key
|
198
|
+
page_solutions[key] = PageRenderer.new.render YAML.load(File.read(path)), :count => signs.length
|
199
|
+
end
|
200
|
+
|
201
|
+
if page_keys.empty?
|
202
|
+
page_keys << 'nada'
|
203
|
+
page_solutions['nada'] = PageRenderer.new.render({'lines' => [{'prefix' => '', 'content' => ['No Pages']}, {'prefix' => '', 'content' => ['Configured']}]}, :count => signs.length)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Loop through pages
|
208
|
+
loop do
|
209
|
+
if stop
|
210
|
+
puts "Inner loop stopping..."
|
211
|
+
break
|
212
|
+
end
|
213
|
+
|
214
|
+
page_key = nil
|
215
|
+
page_solution = nil
|
216
|
+
|
217
|
+
mutex.synchronize do
|
218
|
+
page_index = 0 if page_index >= page_keys.length || page_index < 0
|
219
|
+
|
220
|
+
# check for alert
|
221
|
+
if alert
|
222
|
+
page_key = 'ALERT'
|
223
|
+
page_solution = alert['solution']
|
224
|
+
|
225
|
+
page_index -= 1
|
226
|
+
|
227
|
+
# extract condition_variable
|
228
|
+
condition_variable = alert['condition_variable']
|
229
|
+
|
230
|
+
# clear alert
|
231
|
+
alert = nil
|
232
|
+
|
233
|
+
# signal that we got it!
|
234
|
+
condition_variable.signal
|
235
|
+
else
|
236
|
+
page_key = page_keys[page_index]
|
237
|
+
page_solution = page_solutions[page_key]
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
puts "Sending page #{page_key}"
|
242
|
+
signs.show_page_solution page_solution
|
243
|
+
|
244
|
+
|
245
|
+
sleep_amount = page_solution['lines'] && page_solution['lines'] * 3 * 2 || 20
|
246
|
+
sleep_amount = 20 if sleep_amount < 2
|
247
|
+
|
248
|
+
sleep sleep_amount
|
249
|
+
|
250
|
+
page_index += 1
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
if cv = stop && stop['condition_variable']
|
255
|
+
cv.signal
|
256
|
+
sleep 1 # wait a bit for the CV recipient to finish before we do.
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
desc 'add-page', 'Adds a page to the server rotation'
|
262
|
+
option :page, :required => true, :desc => "Path to page YAML"
|
263
|
+
option :name, :required => true, :desc => "Name for the new file"
|
264
|
+
def add_page
|
265
|
+
exit send_socket_command_expect_ok ['v1', 'add page', options[:name], File.read(options[:page])]
|
266
|
+
end
|
267
|
+
|
268
|
+
desc 'delete-page', 'Deletes a page to the server rotation'
|
269
|
+
option :name, :required => true, :desc => "Name for the new file"
|
270
|
+
def delete_page
|
271
|
+
exit send_socket_command_expect_ok ['v1', 'delete page', options[:name]]
|
272
|
+
end
|
273
|
+
|
274
|
+
desc 'alert', 'Sends a page to display as an alert'
|
275
|
+
option :page, :required => true, :desc => "Path to page YAML"
|
276
|
+
def alert
|
277
|
+
exit send_socket_command_expect_ok ['v1', 'alert', File.read(options[:page])]
|
278
|
+
end
|
279
|
+
|
280
|
+
desc 'stop', 'Stops the running server'
|
281
|
+
def stop
|
282
|
+
exit send_socket_command_expect_ok ['v1', 'stop']
|
283
|
+
end
|
284
|
+
|
285
|
+
private
|
286
|
+
|
287
|
+
def send_socket_command_expect_ok(args)
|
288
|
+
UNIXSocket.open server_socket_path do |socket|
|
289
|
+
send_socket_command(socket, args)
|
290
|
+
puts "Sent message...awaiting reply..."
|
291
|
+
|
292
|
+
(got = socket.gets) && got.strip == "okay" || false
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def send_socket_command(socket, args)
|
297
|
+
socket.sendmsg args.join "\n"
|
298
|
+
socket.flush
|
299
|
+
end
|
300
|
+
|
301
|
+
def server_socket_path
|
302
|
+
File.join(server_settings_path, 'server.sock')
|
303
|
+
end
|
304
|
+
|
305
|
+
def server_settings_path
|
306
|
+
options[:serverrc] || File.join(ENV['HOME'], '.multi_movingsign', 'server')
|
307
|
+
end
|
308
|
+
|
309
|
+
def server_pages_path
|
310
|
+
File.join(server_settings_path, 'pages')
|
311
|
+
end
|
312
|
+
|
313
|
+
def settings_path
|
314
|
+
options[:rc] || Settings.default_settings_path
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|