Class: MultiMovingsign::Server

Inherits:
Thor
  • Object
show all
Defined in:
lib/multi_movingsign/server.rb

Overview

MultiMovingsign server command line interface

Instance Method Summary collapse

Instance Method Details

#add_pageObject



264
265
266
# File 'lib/multi_movingsign/server.rb', line 264

def add_page
  exit send_socket_command_expect_ok ['v1', 'add page', options[:name], File.read(options[:page])]
end

#alertObject



276
277
278
# File 'lib/multi_movingsign/server.rb', line 276

def alert
  exit send_socket_command_expect_ok ['v1', 'alert', File.read(options[:page])]
end

#delete_pageObject



270
271
272
# File 'lib/multi_movingsign/server.rb', line 270

def delete_page
  exit send_socket_command_expect_ok ['v1', 'delete page', options[:name]]
end

#startObject



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/multi_movingsign/server.rb', line 36

def start
  TestRCLoader.load(options['testrc']) if options['testrc']

  # This impl is a hacky mess... FYI!
  FileUtils.mkdir_p server_settings_path

  lock_path = File.join(server_settings_path, "server.lock")
  File.open(lock_path, 'w') do |lock|
    raise "Cannot acquire lock! Is a server already running?" unless lock.flock(File::LOCK_EX | File::LOCK_NB)

    lock.puts $$
    lock.flush

    mutex = Mutex.new

    # setup logging
    log_path = File.join(server_settings_path, "server.log")
    log = File.new(log_path, "a")
    $stdout = TeeIO.new($stdout, log)
    $stderr = TeeIO.new($stderr, log)

    signs = []

    page_keys = []
    page_solutions = {}
    page_index = 0
    alert = nil
    stop = nil

    Thread.new do
      begin
        Socket.unix_server_loop(server_socket_path) do |socket, address|
          puts "SOCKET LOOP!"

          begin
            msg = nil

            begin
              msg, = socket.recvmsg_nonblock
            rescue IO::WaitReadable
              if IO.select([socket], [], [], 5)
                retry
              else
                raise TimeoutError, "Timeout in recvmsg_nonblock"
              end
            end

            unless msg
              $stderr.puts "Bogus unix_server_loop?"
              next
            end

            lines = msg.lines.map { |l| l.rstrip }
            puts "Got UNIX message: #{lines.inspect}"

            version = lines.delete_at 0

            case command = lines.delete_at(0)
              when 'add page'
                name = lines.delete_at(0)
                yaml = lines.join "\n"

                solution = PageRenderer.new.render YAML.load(yaml), :count => signs.length
                page_path = File.join(server_pages_path, "#{name}.yml")
                File.open(page_path, "w") { |f| f.puts yaml }

                mutex.synchronize do
                  page_solutions[name] = solution
                  page_keys << name unless page_keys.include? name

                  puts "Added #{name}!"

                  page_keys.delete 'nada'
                  page_solutions.delete 'nada'
                end

                socket.puts "okay"
              when 'delete page'
                name = lines.delete_at(0)

                mutex.synchronize do
                  page_path = File.join(server_pages_path, "#{name}.yml")

                  FileUtils.rm(page_path, :force => true) if File.exists? page_path

                  page_keys.delete name
                  page_solutions.delete name
                end

                puts "Deleted #{name}"

                socket.puts "okay"
              when 'alert'
                page_yaml = lines.join("\n")

                mutex.synchronize do
                  condition_variable = ConditionVariable.new
                  alert = {"solution" => PageRenderer.new.render(YAML.load(page_yaml), :count => signs.length), 'condition_variable' => condition_variable}

                  puts "Signaling alert..."
                  condition_variable.wait mutex
                end

                socket.puts "okay"

              when 'stop'
                mutex.synchronize do
                  cv = ConditionVariable.new

                  stop = {'condition_variable' => cv}

                  cv.wait mutex
                end

                socket.puts "okay"
              else
                $stderr.puts "Unknown command '#{command}'"
            end
          rescue => e
            $stderr.puts "Exception in unix server loop"
            $stderr.puts e.message
            $stderr.puts e.backtrace.join "\n"
          ensure
            socket.close
            puts "SOCKET CLOSED"
          end
        end
      rescue => e
        $stderr.puts "UNIX socket loop raised!"
        $stderr.puts e.message
        $stderr.puts e.backtrace.join '\n'

        Thread::current.pi
      end
    end

    # Loop to allow reloaded
    loop do
      if stop
        puts "Outter loop stopping..."
        break
      end

      puts "Starting/Reloading!"

      # load sign configuration
      settings = Settings.load settings_path
      raise_no_signs unless settings.signs?

      mutex.synchronize do
        page_keys = []
        page_solutions = {}
        signs = Signs.new settings.signs

        # Load pages and solutions
        FileUtils.mkdir_p server_pages_path
        Dir.glob(File.join(server_pages_path, '*.yml')).sort.each do |path|
          puts "Loading #{path}"

          key = File.basename(path, File.extname(path))

          page_keys << key
          page_solutions[key] = PageRenderer.new.render YAML.load(File.read(path)), :count => signs.length
        end

        if page_keys.empty?
          page_keys << 'nada'
          page_solutions['nada'] = PageRenderer.new.render({'lines' => [{'prefix' => '', 'content' => ['No Pages']}, {'prefix' => '', 'content' => ['Configured']}]}, :count => signs.length)
        end
      end

      # Loop through pages
      loop do
        if stop
          puts "Inner loop stopping..."
          break
        end

        page_key = nil
        page_solution = nil

        mutex.synchronize do
          page_index = 0 if page_index >= page_keys.length || page_index < 0

          # check for alert
          if alert
            page_key = 'ALERT'
            page_solution = alert['solution']

            page_index -= 1

            # extract condition_variable
            condition_variable = alert['condition_variable']

            # clear alert
            alert = nil

            # signal that we got it!
            condition_variable.signal
          else
            page_key = page_keys[page_index]
            page_solution = page_solutions[page_key]
          end
        end

        puts "Sending page #{page_key}"
        signs.show_page_solution page_solution


        sleep_amount = page_solution['lines'] && page_solution['lines'] * 3 * 2 || 20
        sleep_amount = 20 if sleep_amount < 2

        sleep sleep_amount

        page_index += 1
      end
    end

    if cv = stop && stop['condition_variable']
      cv.signal
      sleep 1     # wait a bit for the CV recipient to finish before we do.
    end
  end
end

#stopObject



281
282
283
# File 'lib/multi_movingsign/server.rb', line 281

def stop
  exit send_socket_command_expect_ok ['v1', 'stop']
end