frypan 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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/Rakefile +8 -0
- data/_README.md +31 -0
- data/bin/frypan +55 -0
- data/frypan.gemspec +24 -0
- data/lib/frypan/database_sync_list.rb +30 -0
- data/lib/frypan/epg_parser.rb +23 -0
- data/lib/frypan/node.rb +150 -0
- data/lib/frypan/tuner.rb +47 -0
- data/lib/frypan/version.rb +3 -0
- data/lib/frypan.rb +57 -0
- data/unit_test/test_node.rb +171 -0
- metadata +87 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: fb8a7b23f1048487bc5ab065783383c02ce47b1d
         | 
| 4 | 
            +
              data.tar.gz: 93c85039222d259ad54acab69dadb06e653811f6
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 7af33ae5164675458049fd5be4bc2c7ca67333ca1ca4dd6e525e6575074c905367d092383a5c4bdde9d53269494c76ff8335aa2f86215a86accc1f0152df47df
         | 
| 7 | 
            +
              data.tar.gz: c52cf2274ac18309bf4ecbc09164511dcba3ec2149fb5fd118713f1e66828d78631be153de21a29516920cb1bd1a3d4857e23b581b34bc2b4fbb542fa88adc1a
         | 
    
        data/.gitignore
    ADDED
    
    
    
        data/Gemfile
    ADDED
    
    
    
        data/LICENSE.txt
    ADDED
    
    | @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            Copyright (c) 2015 TODO: Write your name
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            MIT License
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining
         | 
| 6 | 
            +
            a copy of this software and associated documentation files (the
         | 
| 7 | 
            +
            "Software"), to deal in the Software without restriction, including
         | 
| 8 | 
            +
            without limitation the rights to use, copy, modify, merge, publish,
         | 
| 9 | 
            +
            distribute, sublicense, and/or sell copies of the Software, and to
         | 
| 10 | 
            +
            permit persons to whom the Software is furnished to do so, subject to
         | 
| 11 | 
            +
            the following conditions:
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            The above copyright notice and this permission notice shall be
         | 
| 14 | 
            +
            included in all copies or substantial portions of the Software.
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         | 
| 17 | 
            +
            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         | 
| 18 | 
            +
            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         | 
| 19 | 
            +
            NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         | 
| 20 | 
            +
            LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         | 
| 21 | 
            +
            OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         | 
| 22 | 
            +
            WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
    
        data/Rakefile
    ADDED
    
    
    
        data/_README.md
    ADDED
    
    | @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            # Frypan
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            TODO: Write a gem description
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            ## Installation
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Add this line to your application's Gemfile:
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            ```ruby
         | 
| 10 | 
            +
            gem 'frypan'
         | 
| 11 | 
            +
            ```
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            And then execute:
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                $ bundle
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            Or install it yourself as:
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                $ gem install frypan
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            ## Usage
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            TODO: Write usage instructions here
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            ## Contributing
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            1. Fork it ( https://github.com/[my-github-username]/frypan/fork )
         | 
| 28 | 
            +
            2. Create your feature branch (`git checkout -b my-new-feature`)
         | 
| 29 | 
            +
            3. Commit your changes (`git commit -am 'Add some feature'`)
         | 
| 30 | 
            +
            4. Push to the branch (`git push origin my-new-feature`)
         | 
| 31 | 
            +
            5. Create a new Pull Request
         | 
    
        data/bin/frypan
    ADDED
    
    | @@ -0,0 +1,55 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            # -*- coding: utf-8 -*-
         | 
| 3 | 
            +
            # -*- mode: ruby -*-
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            require 'mongo'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            def install(home_path)
         | 
| 8 | 
            +
              # 1. mongodbのバイナリを落として [homepath]/mongodb.tarに保存
         | 
| 9 | 
            +
              # 2. 解凍する。[homepath]/mongodb/bin/mongodってな具合になる
         | 
| 10 | 
            +
              # 3. [homepath]/db/作る
         | 
| 11 | 
            +
            end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            def fork_db(home_path, port=27017)
         | 
| 14 | 
            +
              mongod = "#{home_path}/mongodb/bin/mongod"
         | 
| 15 | 
            +
              db = "#{home_path}/db/"
         | 
| 16 | 
            +
              log = "#{home_path}/db/server.log"
         | 
| 17 | 
            +
              status = `#{mongod} --dbpath #{db} --logpath #{log} --port #{port} --fork`
         | 
| 18 | 
            +
              pid = status.lines.map{|ln| ln.match(/forked process: (\d+)/)}.select{|a| a}.first[1].to_i
         | 
| 19 | 
            +
              success = status.lines.any?{|ln| ln.match(/successfully/)}
         | 
| 20 | 
            +
              if success then pid else nil end
         | 
| 21 | 
            +
            end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            def connect_db(port=27017, host="localhost")
         | 
| 24 | 
            +
              Mongo::Connection.new(host, port).db("frypan_core")
         | 
| 25 | 
            +
            end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            begin
         | 
| 28 | 
            +
              pid = fork_db("~/Workspace/")
         | 
| 29 | 
            +
              TinyFRP::Runtime.new(main_signal(connect_db)).run(:loop)
         | 
| 30 | 
            +
            ensure
         | 
| 31 | 
            +
              Process.kill("TERM", pid) if pid
         | 
| 32 | 
            +
            end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
             | 
| 35 | 
            +
             | 
| 36 | 
            +
            db = hoge
         | 
| 37 | 
            +
            api_server = APIServer.new(db) # fork webrick. only op-command throw.
         | 
| 38 | 
            +
            epg = hoge
         | 
| 39 | 
            +
            tuner = [fuga]
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            command = TinyFRP.lift{ api_server.get_next_command } # one-unit-command OR nil
         | 
| 42 | 
            +
            current_time = TinyFRP.lift{ Time.now.strftime("%Y%m%d%H%M%S") }
         | 
| 43 | 
            +
            epg_flag = hoge
         | 
| 44 | 
            +
            tuners_flag = [fuga]
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            main = TinyFRP::Bundle.new(command, current_time, epg_flag, tuners_flag) >> Node.top(setting)
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            TinyFRP.loop(main) do |out|
         | 
| 49 | 
            +
              out[:reserve1].action(db)
         | 
| 50 | 
            +
              out[:reserve2].action(db)
         | 
| 51 | 
            +
              out[:reserve3].action(db)
         | 
| 52 | 
            +
              out[:reserve4].action(db)
         | 
| 53 | 
            +
              out[:epg].action(epg)
         | 
| 54 | 
            +
              out[:tuner1].action(tuner1)
         | 
| 55 | 
            +
            end
         | 
    
        data/frypan.gemspec
    ADDED
    
    | @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            # -*- mode: ruby -*-
         | 
| 2 | 
            +
            # coding: utf-8
         | 
| 3 | 
            +
            lib = File.expand_path('../lib', __FILE__)
         | 
| 4 | 
            +
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         | 
| 5 | 
            +
            require 'frypan/version'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Gem::Specification.new do |spec|
         | 
| 8 | 
            +
              spec.name          = "frypan"
         | 
| 9 | 
            +
              spec.version       = Frypan::VERSION
         | 
| 10 | 
            +
              spec.authors       = ["sawaken"]
         | 
| 11 | 
            +
              spec.email         = ["sasasawada@gmail.com"]
         | 
| 12 | 
            +
              spec.summary       = %q{Yet another recording scheduler.}
         | 
| 13 | 
            +
              spec.description   = %q{}
         | 
| 14 | 
            +
              spec.homepage      = ""
         | 
| 15 | 
            +
              spec.license       = "MIT"
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              spec.files         = `git ls-files -z`.split("\x0")
         | 
| 18 | 
            +
              spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
         | 
| 19 | 
            +
              spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
         | 
| 20 | 
            +
              spec.require_paths = ["lib"]
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              spec.add_development_dependency "bundler", "~> 1.7"
         | 
| 23 | 
            +
              spec.add_development_dependency "rake", "~> 10.0"
         | 
| 24 | 
            +
            end
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            module Frypan
         | 
| 2 | 
            +
              class DBSyncList
         | 
| 3 | 
            +
                def initialize(name, db)
         | 
| 4 | 
            +
                  @coll = db.collection(name)
         | 
| 5 | 
            +
                end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def +(list)
         | 
| 8 | 
            +
                  list.each{|a| @coll.insert(a)}
         | 
| 9 | 
            +
                  self
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def update(alt_list)
         | 
| 13 | 
            +
                  @coll.remove
         | 
| 14 | 
            +
                  alt_list.each{|a| @coll.insert(a)}
         | 
| 15 | 
            +
                  self
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def update_elements(elements, ident_key)
         | 
| 19 | 
            +
                  elements.each do |a|
         | 
| 20 | 
            +
                    @coll.update({ident_key => a[ident_key]}, {"$set" => a}, true)
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                  self
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def to_a
         | 
| 26 | 
            +
                  @coll.find.to_a
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
            end
         | 
| 30 | 
            +
             | 
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            module Frypan
         | 
| 2 | 
            +
              class EpgParser
         | 
| 3 | 
            +
                require 'm2ts_parser'
         | 
| 4 | 
            +
                class ParserProcess
         | 
| 5 | 
            +
                  def initialize(file_path)
         | 
| 6 | 
            +
                    
         | 
| 7 | 
            +
                  end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def finished?
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def get_parsed
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def parse(file_path)
         | 
| 19 | 
            +
                  ParserProcess.new(file_path)
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
            end
         | 
| 23 | 
            +
             | 
    
        data/lib/frypan/node.rb
    ADDED
    
    | @@ -0,0 +1,150 @@ | |
| 1 | 
            +
            module Frypan
         | 
| 2 | 
            +
              require 'tiny_frp'
         | 
| 3 | 
            +
              module Node
         | 
| 4 | 
            +
                def self.overlaped?(list_of_range)
         | 
| 5 | 
            +
                  !list_of_range.sort{|a, b| a.begin <=> b.begin}.each_cons(2).all?{|a, b| a.end <= b.begin}
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def self.apply_operations(list, ops)
         | 
| 9 | 
            +
                  case
         | 
| 10 | 
            +
                  when ops == []
         | 
| 11 | 
            +
                    [list, ""]
         | 
| 12 | 
            +
                  when ops.first[:kind] == :add
         | 
| 13 | 
            +
                    added_list = list + [ops.first[:target]]
         | 
| 14 | 
            +
                    if overlaped?(added_list.map{|a| a[:start_time]..a[:end_time]})
         | 
| 15 | 
            +
                      [nil, "cannot add reservation: #{ops.first[:target]}"]
         | 
| 16 | 
            +
                    else
         | 
| 17 | 
            +
                      apply_operations(added_list, ops.drop(1))
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
                  when ops.first[:kind] == :remove
         | 
| 20 | 
            +
                    if list.find{|a| a[:start_time] == ops.first[:target][:start_time]}
         | 
| 21 | 
            +
                      apply_operations(list.reject{|a| a[:start_time] == ops.first[:target][:start_time]}, ops.drop(1))
         | 
| 22 | 
            +
                    else
         | 
| 23 | 
            +
                      [nil, "unexist remove reservation id: #{ops.first[:target][:id]}"]
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
                  else
         | 
| 26 | 
            +
                    [nil, "unknown operation '#{ops.first[:kind]}'"]
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def self.ReserveListFoldp(tuner_id, initial_list=[])
         | 
| 31 | 
            +
                  TinyFRP.foldp(launch: nil, list: initial_list, ack: nil) do |acc, command, current_time|
         | 
| 32 | 
            +
                    case
         | 
| 33 | 
            +
                    when command && command[:target_tuner] == tuner_id
         | 
| 34 | 
            +
                      new_list, status = *apply_operations(acc[:list], command[:operations])
         | 
| 35 | 
            +
                      ack = command.merge({
         | 
| 36 | 
            +
                         timestamp: current_time,
         | 
| 37 | 
            +
                         succeeded: !!new_list,
         | 
| 38 | 
            +
                         status: status
         | 
| 39 | 
            +
                       })
         | 
| 40 | 
            +
                      if new_list
         | 
| 41 | 
            +
                        {launch: nil, list: new_list.sort{|a, b| a[:start_time] <=> b[:start_time]}, ack: ack}
         | 
| 42 | 
            +
                      else
         | 
| 43 | 
            +
                        {launch: nil, ack: ack}
         | 
| 44 | 
            +
                      end
         | 
| 45 | 
            +
                    when acc[:list] != [] && acc[:list].first[:start_time] <= current_time
         | 
| 46 | 
            +
                      {launch: acc[:list].first, list: acc[:list].drop(1), ack: nil}
         | 
| 47 | 
            +
                    else
         | 
| 48 | 
            +
                      {launch: nil, ack: nil}
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def self.PersistantReserveListFoldp(tuner_id, dbsync_reserve)
         | 
| 54 | 
            +
                  TinyFRP.foldp(last_list: nil, list: dbsync_reserve) do |acc, rlist|
         | 
| 55 | 
            +
                    if !acc[:last_list] || acc[:last_list] != rlist[:list]
         | 
| 56 | 
            +
                      {last_list: rlist[:list], list: acc[:list].update(rlist[:list])}
         | 
| 57 | 
            +
                    else
         | 
| 58 | 
            +
                      {}
         | 
| 59 | 
            +
                    end
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def self.RecordingAsyncFoldp(tuner)
         | 
| 64 | 
            +
                  TinyFRP.foldp(busy: false, rec_process: nil, waiting: [], log: []) do |acc, rlist|
         | 
| 65 | 
            +
                    case
         | 
| 66 | 
            +
                    when rlist[:launch]
         | 
| 67 | 
            +
                      {waiting: acc[:waiting] + [rlist[:launch]], log: []}
         | 
| 68 | 
            +
                    when acc[:busy]
         | 
| 69 | 
            +
                      {busy: !acc[:rec_process].finished?, log: acc[:rec_process].get_log}
         | 
| 70 | 
            +
                    when acc[:waiting] != []
         | 
| 71 | 
            +
                      {
         | 
| 72 | 
            +
                        busy:        true,
         | 
| 73 | 
            +
                        rec_process: tuner.rec(acc[:waiting].first),
         | 
| 74 | 
            +
                        waiting:     acc[:waiting].drop(1),
         | 
| 75 | 
            +
                        log:         []
         | 
| 76 | 
            +
                      }
         | 
| 77 | 
            +
                    else
         | 
| 78 | 
            +
                      {log: []}
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
                end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                def self.RecordingLogFoldp(initial_log=[])
         | 
| 84 | 
            +
                  TinyFRP.foldp(log: initial_log, epg_order: [], recorded: []) do |acc, *log_inputs|
         | 
| 85 | 
            +
                    if log_inputs.any?{|a| a != []}
         | 
| 86 | 
            +
                      new_logs = log_inputs.select{|a| a != []}.flatten
         | 
| 87 | 
            +
                      {
         | 
| 88 | 
            +
                        log:       acc[:log] + new_logs,
         | 
| 89 | 
            +
                        epg_order: Fnc.find_epg_order(new_logs),
         | 
| 90 | 
            +
                        recorded:  Fnc.find_recorded(new_logs)
         | 
| 91 | 
            +
                      }
         | 
| 92 | 
            +
                    else
         | 
| 93 | 
            +
                      {epg_order: [], recorded: []}
         | 
| 94 | 
            +
                    end
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                def self.EpgExtractAsyncFoldp(epg_parser)
         | 
| 99 | 
            +
                  TinyFRP.foldp(busy: false, parser_process: nil, waiting: [], extracted: []) do |acc, log|
         | 
| 100 | 
            +
                    case
         | 
| 101 | 
            +
                    when log[:epg_order]
         | 
| 102 | 
            +
                      {waiting: acc[:waiting] + [log[:epg_order]], extracted: []}
         | 
| 103 | 
            +
                    when acc[:busy]
         | 
| 104 | 
            +
                      {busy: !acc[:parser_process].finished?, extracted: acc[:parser_process].get_parsed}
         | 
| 105 | 
            +
                    when acc[:waiting] != []
         | 
| 106 | 
            +
                      {
         | 
| 107 | 
            +
                        busy:           true,
         | 
| 108 | 
            +
                        parser_process: epg_parser.parse(acc[:waiting].first[:file_path]), 
         | 
| 109 | 
            +
                        waiting:        acc[:waiting].drop(1),
         | 
| 110 | 
            +
                        extracted:      []
         | 
| 111 | 
            +
                      }
         | 
| 112 | 
            +
                    else
         | 
| 113 | 
            +
                      {extracted: []}
         | 
| 114 | 
            +
                    end
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                def self.ProgramListFoldp(initial_list=[])
         | 
| 119 | 
            +
                  TinyFRP.foldp(list: initial_list) do |acc, epg|
         | 
| 120 | 
            +
                    if epg[:extracted] != []
         | 
| 121 | 
            +
                      # epg[:extracted] := list of {program_id: [service_id, event_id], ...}
         | 
| 122 | 
            +
                      {list: acc[:list].update_elements(epg[:extracted], :program_id)}
         | 
| 123 | 
            +
                    else
         | 
| 124 | 
            +
                      {}
         | 
| 125 | 
            +
                    end
         | 
| 126 | 
            +
                  end
         | 
| 127 | 
            +
                end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                def self.RecordedListFoldp(initial_list=[])
         | 
| 130 | 
            +
                  TinyFRP.foldp(list: initial_list) do |acc, log|
         | 
| 131 | 
            +
                    if log[:recorded] != []
         | 
| 132 | 
            +
                      {list: acc[:list] + log[:recorded]}
         | 
| 133 | 
            +
                    else
         | 
| 134 | 
            +
                      {}
         | 
| 135 | 
            +
                    end
         | 
| 136 | 
            +
                  end
         | 
| 137 | 
            +
                end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                def self.UIresponseFoldp(initial_list=[])
         | 
| 140 | 
            +
                  TinyFRP.foldp(list: initial_list) do |acc, current_time, *acks|
         | 
| 141 | 
            +
                    if acks.any?{|a| a}
         | 
| 142 | 
            +
                      {list: acc[:list] + acks.select{|a| a}}
         | 
| 143 | 
            +
                    else
         | 
| 144 | 
            +
                      {}
         | 
| 145 | 
            +
                    end
         | 
| 146 | 
            +
                  end
         | 
| 147 | 
            +
                end
         | 
| 148 | 
            +
              end
         | 
| 149 | 
            +
            end
         | 
| 150 | 
            +
             | 
    
        data/lib/frypan/tuner.rb
    ADDED
    
    | @@ -0,0 +1,47 @@ | |
| 1 | 
            +
            module Frypan
         | 
| 2 | 
            +
              class Tuner
         | 
| 3 | 
            +
                class RecordingProcess
         | 
| 4 | 
            +
                  def initialize(command)
         | 
| 5 | 
            +
                    @reader, writer = IO.pipe
         | 
| 6 | 
            +
                    @pid = Process.spawn(command, :err => writer, :out => writer)
         | 
| 7 | 
            +
                    @mutex = Mutex.new
         | 
| 8 | 
            +
                    @logs = []
         | 
| 9 | 
            +
                    Thread.new{
         | 
| 10 | 
            +
                      while IO::select([@reader])
         | 
| 11 | 
            +
                        @mutex.synchronize{
         | 
| 12 | 
            +
                          @logs << @reader.gets.chomp
         | 
| 13 | 
            +
                        }
         | 
| 14 | 
            +
                      end
         | 
| 15 | 
            +
                    }
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def finished?
         | 
| 19 | 
            +
                    @finished ||= Process.waitpid2(@pid, Process::WNOHANG)
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def get_log
         | 
| 23 | 
            +
                    res = []
         | 
| 24 | 
            +
                    @mutex.synchronize{
         | 
| 25 | 
            +
                      res = @logs.dup
         | 
| 26 | 
            +
                      @logs = []
         | 
| 27 | 
            +
                    }
         | 
| 28 | 
            +
                    res
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def initialize(format_str)
         | 
| 33 | 
            +
                  @format_str = format_str
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                def rec(setting)
         | 
| 37 | 
            +
                  RecordingProcess.new(Tuner.make_command(@format_str, setting))
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                def self.make_command(format_str, setting)
         | 
| 41 | 
            +
                  setting.keys.inject(format_str) do |str, key|
         | 
| 42 | 
            +
                    str.gsub("<#{key}>", setting[key].to_s)
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
            end
         | 
| 47 | 
            +
             | 
    
        data/lib/frypan.rb
    ADDED
    
    | @@ -0,0 +1,57 @@ | |
| 1 | 
            +
            require "frypan/version"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Frypan
         | 
| 4 | 
            +
              # TODO
         | 
| 5 | 
            +
              #  imp APIServer
         | 
| 6 | 
            +
              #  imp EpgParser
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def self.main_signal(db, tuner_config=nil)
         | 
| 9 | 
            +
                dbsync_ui_res        = DBSyncList.new("ui_response",   db)
         | 
| 10 | 
            +
                dbsync_recording_log = DBSyncList.new("recording_log", db)
         | 
| 11 | 
            +
                dbsync_program       = DBSyncList.new("program",       db)
         | 
| 12 | 
            +
                dbsync_recorded      = DBSyncList.new("recorded",      db)
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                dbsync_reserve1 = DBSyncList.new("reserve1", db)
         | 
| 15 | 
            +
                dbsync_reserve2 = DBSyncList.new("reserve2", db)
         | 
| 16 | 
            +
                dbsync_reserve3 = DBSyncList.new("reserve3", db)
         | 
| 17 | 
            +
                dbsync_reserve4 = DBSyncList.new("reserve4", db)
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                api_server = APIServer.new(db) # fork webrick. only op-command throw.
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                command = TinyFRP.lift{ api_server.get_next_command } # one-unit-command OR nil
         | 
| 22 | 
            +
                #com = {
         | 
| 23 | 
            +
                #  command_id: 1024,
         | 
| 24 | 
            +
                #  target_tuner: 1
         | 
| 25 | 
            +
                #  operations: [
         | 
| 26 | 
            +
                #               {:kind => :remove, ::target => {reserve data (something including start_time)}},
         | 
| 27 | 
            +
                #               {:kind => :add,    :target => {reserve data}}
         | 
| 28 | 
            +
                #              ]
         | 
| 29 | 
            +
                #}
         | 
| 30 | 
            +
                  
         | 
| 31 | 
            +
                current_time = TinyFRP.lift{ Time.now.strftime("%Y%m%d%H%M%S") }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                s1 = TinyFRP.bundle(command, current_time) >> Node.ReserveListFoldp("tuner1", dbsync_reserve1.to_a)
         | 
| 34 | 
            +
                s2 = TinyFRP.bundle(command, current_time) >> Node.ReserveListFoldp("tuner2", dbsync_reserve2.to_a)
         | 
| 35 | 
            +
                s3 = TinyFRP.bundle(command, current_time) >> Node.ReserveListFoldp("tuner3", dbsync_reserve3.to_a)
         | 
| 36 | 
            +
                s4 = TinyFRP.bundle(command, current_time) >> Node.ReserveListFoldp("tuner4", dbsync_reserve4.to_a)
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                ui_res = Node.UIresponseFoldp(dbsync_ui_res) <<  TinyFRP.bundle(s1, s2, s3, s4)
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                ps1 = s1 >> Node.PersistantReserveListFoldp("tuner1", dbsync_reserve1)
         | 
| 41 | 
            +
                ps2 = s2 >> Node.PersistantReserveListFoldp("tuner2", dbsync_reserve2)
         | 
| 42 | 
            +
                ps3 = s3 >> Node.PersistantReserveListFoldp("tuner3", dbsync_reserve3)
         | 
| 43 | 
            +
                ps4 = s4 >> Node.PersistantReserveListFoldp("tuner4", dbsync_reserve4)
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                t1 = s1 >> Node.RecordingAsyncFoldp(Tuner.new("fmt1"))
         | 
| 46 | 
            +
                t2 = s2 >> Node.RecordingAsyncFoldp(Tuner.new("fmt2"))
         | 
| 47 | 
            +
                t3 = s3 >> Node.RecordingAsyncFoldp(Tuner.new("fmt2"))
         | 
| 48 | 
            +
                t4 = s4 >> Node.RecordingAsyncFoldp(Tuner.new("fmt2"))
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                recording_log = TinyFRP.bundle(t1, t2, t3, t4) >> Node.RecordingLogFoldp(dbsync_recording_log)
         | 
| 51 | 
            +
                epg_extract   = recording_log >> Node.EpgExtractAsyncFoldp(EpgParser.new)
         | 
| 52 | 
            +
                program_list  = epg_extract >> Node.ProgramListFoldp(dbsync_program)
         | 
| 53 | 
            +
                recorded_list = recording_log >> Node.RecordedListFoldp(dbsync_recorded)
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                return TinyFRP.bundle(ui_res, ps1, ps2, ps3, ps4, program_list, recorded_list) >> TinyFRP.bottom
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
            end
         | 
| @@ -0,0 +1,171 @@ | |
| 1 | 
            +
            $LIBRARY_ROOT_PATH = File.dirname(File.expand_path(File.dirname(__FILE__)))
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Frypan
         | 
| 4 | 
            +
              module UnitTest
         | 
| 5 | 
            +
                require 'test/unit'
         | 
| 6 | 
            +
                require $LIBRARY_ROOT_PATH + '/lib/frypan/node.rb'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                class DummyList
         | 
| 9 | 
            +
                  attr_reader :array
         | 
| 10 | 
            +
                  def initialize(init=[])
         | 
| 11 | 
            +
                    @array = []
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def +(list)
         | 
| 15 | 
            +
                    DummyList.new(@array + list)
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def update(alt_list)
         | 
| 19 | 
            +
                    DummyList.new(alt_list)
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def update_elements(elements, ident_key)
         | 
| 23 | 
            +
                    if elements == []
         | 
| 24 | 
            +
                      self
         | 
| 25 | 
            +
                    else
         | 
| 26 | 
            +
                      update_element(elements.first, ident_key).update_elements(elements.drop(1), ident_key)
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  def update_element(element, ident_key)
         | 
| 31 | 
            +
                    if @array.find{|a| a[ident_key] == element[ident_key]}
         | 
| 32 | 
            +
                      DummyList.new(@array.map{|a| a[ident_key] == element[ident_key] ? element : a})
         | 
| 33 | 
            +
                    else
         | 
| 34 | 
            +
                      DummyList.new(@array + [element])
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                class DummyTuner
         | 
| 40 | 
            +
                  class DummyRecordingProcess
         | 
| 41 | 
            +
                    def initialize(logs)
         | 
| 42 | 
            +
                      @logs = logs.dup
         | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    def finished?
         | 
| 46 | 
            +
                      @logs.empty?
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                    
         | 
| 49 | 
            +
                    def get_log
         | 
| 50 | 
            +
                      @logs.shift || []
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  def initialize(logs)
         | 
| 55 | 
            +
                    @logs = logs
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  def rec(setting)
         | 
| 59 | 
            +
                    DummyRecordingProcess.new(@logs)
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                class DummyEpgParser
         | 
| 64 | 
            +
                  class DummyParserProcess
         | 
| 65 | 
            +
                    def initialize(parseds)
         | 
| 66 | 
            +
                      @parseds = parseds.dup
         | 
| 67 | 
            +
                    end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    def finished?
         | 
| 70 | 
            +
                      @parseds.empty?
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                    def get_parsed
         | 
| 74 | 
            +
                      @parseds.shift || []
         | 
| 75 | 
            +
                    end
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  def initialize(parseds)
         | 
| 79 | 
            +
                    @parseds = parseds
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  def parse(file_path)
         | 
| 83 | 
            +
                    DummyParserProcess.new(@parseds)
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                module Util
         | 
| 88 | 
            +
                  def mkcom(command_id, target_tuner, *ops)
         | 
| 89 | 
            +
                    {command_id: command_id, target_tuner: target_tuner, operations: ops}
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  def mkop(kind, target)
         | 
| 93 | 
            +
                    {kind: kind, target: target}
         | 
| 94 | 
            +
                  end
         | 
| 95 | 
            +
                  
         | 
| 96 | 
            +
                  def mkres(id, start_time, end_time)
         | 
| 97 | 
            +
                    {id: id, start_time: start_time, end_time: end_time}
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                class NodeTest < Test::Unit::TestCase
         | 
| 102 | 
            +
                  include Util
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                  def test_ReserveListFoldp
         | 
| 105 | 
            +
                    n = Node.ReserveListFoldp(:test_tuner, [])
         | 
| 106 | 
            +
                    vals = [
         | 
| 107 | 
            +
                            nil,
         | 
| 108 | 
            +
                            com1 = mkcom(0, :test_tuner, mkop(:add, res1 = mkres(0, 2, 5))),
         | 
| 109 | 
            +
                            com2 = mkcom(1, :test_tuner, mkop(:add, res2 = mkres(1, 5, 7))),
         | 
| 110 | 
            +
                            nil,
         | 
| 111 | 
            +
                            com3 = mkcom(2, :test_tuner, mkop(:add, res3 = mkres(2, 6, 10)))
         | 
| 112 | 
            +
                           ].each_with_index.map{|a, i| n.call(a, i)}
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                    assert_equal({launch: nil, list: [], ack: nil}, vals[0])
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                    expect_ack1 = com1.merge(timestamp: 1, succeeded: true, status: "")
         | 
| 117 | 
            +
                    assert_equal({launch: nil,  list: [res1], ack: expect_ack1}, vals[1])
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                    expect_ack2 = com2.merge(timestamp: 2, succeeded: true, status: "")
         | 
| 120 | 
            +
                    assert_equal({launch: nil, list: [res1, res2], ack: expect_ack2}, vals[2])
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                    assert_equal({launch: res1,  list: [res2], ack: nil}, vals[3])
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                    expect_ack3 = com3.merge(timestamp: 4, succeeded: false, status: vals[4][:ack][:status])
         | 
| 125 | 
            +
                    assert_equal({launch: nil, list: [res2], ack: expect_ack3}, vals[4])
         | 
| 126 | 
            +
                  end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                  def test_PersistantReserveListFoldp
         | 
| 129 | 
            +
                    n = Node.PersistantReserveListFoldp(:tuner_tuner, DummyList.new([]))
         | 
| 130 | 
            +
                    vals = [
         | 
| 131 | 
            +
                            {list: [:a]},
         | 
| 132 | 
            +
                            {list: [:a]},
         | 
| 133 | 
            +
                            {list: [:a, :b]},
         | 
| 134 | 
            +
                           ].map{|a| n.call(a)}
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                    assert_equal(vals[0], vals[1])
         | 
| 137 | 
            +
                    assert_not_equal(vals[1], vals[2])
         | 
| 138 | 
            +
                  end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                  def test_RecordingAsyncFoldp
         | 
| 141 | 
            +
                    n = Node.RecordingAsyncFoldp(DummyTuner.new([[0, 1], [2]]))
         | 
| 142 | 
            +
                    v = [
         | 
| 143 | 
            +
                         {launch: nil},
         | 
| 144 | 
            +
                         {launch: :a},
         | 
| 145 | 
            +
                         {launch: nil},
         | 
| 146 | 
            +
                         {launch: :b},
         | 
| 147 | 
            +
                         {launch: nil},
         | 
| 148 | 
            +
                         {launch: nil},
         | 
| 149 | 
            +
                         {launch: nil},
         | 
| 150 | 
            +
                         {launch: nil},
         | 
| 151 | 
            +
                         {launch: nil},
         | 
| 152 | 
            +
                         {launch: nil},
         | 
| 153 | 
            +
                         {launch: nil},
         | 
| 154 | 
            +
                        ].map{|a| n.call(a)}
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                    assert_equal({busy: false, rec_process: v[0][:rec_process],  waiting: [],   log: []},     v[0])
         | 
| 157 | 
            +
                    assert_equal({busy: false, rec_process: v[1][:rec_process],  waiting: [:a], log: []},     v[1])
         | 
| 158 | 
            +
                    assert_equal({busy: true,  rec_process: v[2][:rec_process],  waiting: [],   log: []},     v[2])
         | 
| 159 | 
            +
                    assert_equal({busy: true,  rec_process: v[3][:rec_process],  waiting: [:b], log: []},     v[3])
         | 
| 160 | 
            +
                    assert_equal({busy: true,  rec_process: v[4][:rec_process],  waiting: [:b], log: [0, 1]}, v[4])
         | 
| 161 | 
            +
                    assert_equal({busy: true,  rec_process: v[5][:rec_process],  waiting: [:b], log: [2]},    v[5])
         | 
| 162 | 
            +
                    assert_equal({busy: false, rec_process: v[6][:rec_process],  waiting: [:b], log: []},     v[6])
         | 
| 163 | 
            +
                    assert_equal({busy: true,  rec_process: v[7][:rec_process],  waiting: [],   log: []},     v[7])
         | 
| 164 | 
            +
                    assert_equal({busy: true,  rec_process: v[8][:rec_process],  waiting: [],   log: [0, 1]}, v[8])
         | 
| 165 | 
            +
                    assert_equal({busy: true,  rec_process: v[9][:rec_process],  waiting: [],   log: [2]},    v[9])
         | 
| 166 | 
            +
                    assert_equal({busy: false, rec_process: v[10][:rec_process], waiting: [],   log: []},     v[10])
         | 
| 167 | 
            +
                  end
         | 
| 168 | 
            +
                end
         | 
| 169 | 
            +
              end
         | 
| 170 | 
            +
            end
         | 
| 171 | 
            +
             | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,87 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: frypan
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.0.1
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - sawaken
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2015-02-04 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: bundler
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - "~>"
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '1.7'
         | 
| 20 | 
            +
              type: :development
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '1.7'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: rake
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - "~>"
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '10.0'
         | 
| 34 | 
            +
              type: :development
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - "~>"
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '10.0'
         | 
| 41 | 
            +
            description: ''
         | 
| 42 | 
            +
            email:
         | 
| 43 | 
            +
            - sasasawada@gmail.com
         | 
| 44 | 
            +
            executables:
         | 
| 45 | 
            +
            - frypan
         | 
| 46 | 
            +
            extensions: []
         | 
| 47 | 
            +
            extra_rdoc_files: []
         | 
| 48 | 
            +
            files:
         | 
| 49 | 
            +
            - ".gitignore"
         | 
| 50 | 
            +
            - Gemfile
         | 
| 51 | 
            +
            - LICENSE.txt
         | 
| 52 | 
            +
            - Rakefile
         | 
| 53 | 
            +
            - _README.md
         | 
| 54 | 
            +
            - bin/frypan
         | 
| 55 | 
            +
            - frypan.gemspec
         | 
| 56 | 
            +
            - lib/frypan.rb
         | 
| 57 | 
            +
            - lib/frypan/database_sync_list.rb
         | 
| 58 | 
            +
            - lib/frypan/epg_parser.rb
         | 
| 59 | 
            +
            - lib/frypan/node.rb
         | 
| 60 | 
            +
            - lib/frypan/tuner.rb
         | 
| 61 | 
            +
            - lib/frypan/version.rb
         | 
| 62 | 
            +
            - unit_test/test_node.rb
         | 
| 63 | 
            +
            homepage: ''
         | 
| 64 | 
            +
            licenses:
         | 
| 65 | 
            +
            - MIT
         | 
| 66 | 
            +
            metadata: {}
         | 
| 67 | 
            +
            post_install_message: 
         | 
| 68 | 
            +
            rdoc_options: []
         | 
| 69 | 
            +
            require_paths:
         | 
| 70 | 
            +
            - lib
         | 
| 71 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 72 | 
            +
              requirements:
         | 
| 73 | 
            +
              - - ">="
         | 
| 74 | 
            +
                - !ruby/object:Gem::Version
         | 
| 75 | 
            +
                  version: '0'
         | 
| 76 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 77 | 
            +
              requirements:
         | 
| 78 | 
            +
              - - ">="
         | 
| 79 | 
            +
                - !ruby/object:Gem::Version
         | 
| 80 | 
            +
                  version: '0'
         | 
| 81 | 
            +
            requirements: []
         | 
| 82 | 
            +
            rubyforge_project: 
         | 
| 83 | 
            +
            rubygems_version: 2.2.2
         | 
| 84 | 
            +
            signing_key: 
         | 
| 85 | 
            +
            specification_version: 4
         | 
| 86 | 
            +
            summary: Yet another recording scheduler.
         | 
| 87 | 
            +
            test_files: []
         |