vmsim 0.2.3
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/.autotest +27 -0
- data/README.txt +1 -0
- data/Rakefile +40 -0
- data/VERSION +1 -0
- data/bin/vmdeploy +23 -0
- data/bin/vmsim +37 -0
- data/doc/actuators_and_sensors.txt +32 -0
- data/doc/handout.odt +0 -0
- data/doc/machine.jpg +0 -0
- data/doc/todo.txt +25 -0
- data/lib/adapter/actuator_collection.rb +61 -0
- data/lib/adapter/sensor_collection.rb +57 -0
- data/lib/archive_builder.rb +64 -0
- data/lib/control/main.rb +21 -0
- data/lib/control.rb +3 -0
- data/lib/deploy/deployer.rb +96 -0
- data/lib/deploy.rb +1 -0
- data/lib/devices/devices.rb +73 -0
- data/lib/devices.rb +1 -0
- data/lib/gui/SimulatorGui.gtk +289 -0
- data/lib/gui/simulator_gtk.rb +57 -0
- data/lib/gui/simulator_gui.rb +64 -0
- data/lib/gui.rb +2 -0
- data/lib/hardware/bin.rb +28 -0
- data/lib/hardware/button.rb +18 -0
- data/lib/hardware/can.rb +9 -0
- data/lib/hardware/cash_register.rb +79 -0
- data/lib/hardware/component.rb +56 -0
- data/lib/hardware/display.rb +29 -0
- data/lib/hardware/drawer.rb +45 -0
- data/lib/hardware/enum.rb +20 -0
- data/lib/hardware/simulator.rb +98 -0
- data/lib/hardware.rb +10 -0
- data/lib/hardware_adapter.rb +11 -0
- data/lib/vmlog.rb +5 -0
- data/test/adapter/actuator_collection_test.rb +106 -0
- data/test/adapter/sensor_collection_test.rb +51 -0
- data/test/deploy/deployer_test.rb +114 -0
- data/test/deploy/was_run.sh +5 -0
- data/test/devices/devices_test.rb +78 -0
- data/test/hardware/bin_test.rb +57 -0
- data/test/hardware/button_test.rb +23 -0
- data/test/hardware/can_test.rb +11 -0
- data/test/hardware/cash_register_test.rb +157 -0
- data/test/hardware/component_test.rb +37 -0
- data/test/hardware/display_test.rb +94 -0
- data/test/hardware/drawer_test.rb +100 -0
- data/test/hardware/enum_test.rb +33 -0
- data/test/hardware/simulator_test.rb +189 -0
- data/test/test_helper.rb +5 -0
- data/vmsim.gemspec +89 -0
- metadata +131 -0
    
        data/.autotest
    ADDED
    
    | @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            class Autotest
         | 
| 2 | 
            +
              def path_to_classname(s)
         | 
| 3 | 
            +
                sep = File::SEPARATOR
         | 
| 4 | 
            +
                f = s.sub(/^test#{sep}/, '').sub(/\.rb$/, '').split(sep)
         | 
| 5 | 
            +
                f = f.map { |path| path.split(/_|(\d+)/).map { |seg| seg.capitalize }.join }
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                f.join('::')
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
            end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
             | 
| 12 | 
            +
            Autotest.add_hook :initialize do |autotest|
         | 
| 13 | 
            +
                autotest.add_mapping(%r%^test/.*\.rb$%) do |filename, _|
         | 
| 14 | 
            +
                    filename
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
                autotest.add_mapping(%r%^lib/(.*)\.rb$%) do |_, m|
         | 
| 17 | 
            +
                    ["test/#{m[1]}_test.rb"]
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
                autotest.add_mapping(%r%^test/test_helper.rb$%) do
         | 
| 20 | 
            +
                    files_matching %r%^test/.*_test\.rb$%
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
            end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
             | 
| 25 | 
            +
            Autotest.add_discovery do
         | 
| 26 | 
            +
              "testunit"
         | 
| 27 | 
            +
            end
         | 
    
        data/README.txt
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            vendingmachine exercise in Ruby
         | 
    
        data/Rakefile
    ADDED
    
    | @@ -0,0 +1,40 @@ | |
| 1 | 
            +
            require 'rubygems'
         | 
| 2 | 
            +
            #Gem::manage_gems
         | 
| 3 | 
            +
            require 'rubygems/package_task'
         | 
| 4 | 
            +
            require 'rake/testtask'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
             | 
| 7 | 
            +
            desc 'Running all unit tests'
         | 
| 8 | 
            +
            task :default => :test
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            Rake::TestTask.new(:test) do |t|
         | 
| 11 | 
            +
              t.test_files = FileList['test/**/*test.rb', 'test/*test.rb']
         | 
| 12 | 
            +
            end
         | 
| 13 | 
            +
             
         | 
| 14 | 
            +
            desc 'Measures test coverage'
         | 
| 15 | 
            +
            task :coverage do
         | 
| 16 | 
            +
               rm_f "coverage"
         | 
| 17 | 
            +
               rm_f "coverage.data"
         | 
| 18 | 
            +
               rcov = "rcov --aggregate coverage.data"
         | 
| 19 | 
            +
               system("#{rcov} --html test/*test.rb")
         | 
| 20 | 
            +
            end
         | 
| 21 | 
            +
             
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            require 'jeweler'
         | 
| 24 | 
            +
            Jeweler::Tasks.new do |gem|
         | 
| 25 | 
            +
              # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
         | 
| 26 | 
            +
              gem.name = "vmsim"
         | 
| 27 | 
            +
              gem.executables << 'vmsim'
         | 
| 28 | 
            +
              gem.executables << 'vmdeploy'
         | 
| 29 | 
            +
              gem.license = "MIT"
         | 
| 30 | 
            +
              gem.summary = %Q{Vending machine simulator - part of the vendingmachine tdd bdd case}
         | 
| 31 | 
            +
              gem.description = %Q{Vending machine; this simulator starts a vendingmachine ui which behaviour can be programmed with control code}
         | 
| 32 | 
            +
              gem.email = "info@qwan.it"
         | 
| 33 | 
            +
              gem.authors = ["Rob Westgeest, Marc Evers, Willem van den Ende"]
         | 
| 34 | 
            +
              # Include your dependencies below. Runtime dependencies are required when using your gem,
         | 
| 35 | 
            +
              # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
         | 
| 36 | 
            +
              #gem.add_runtime_dependency 'gtk2', '>= 1.0.3'
         | 
| 37 | 
            +
              gem.add_development_dependency 'mocha', '> 0.9.8'
         | 
| 38 | 
            +
            end
         | 
| 39 | 
            +
            Jeweler::RubygemsDotOrgTasks.new
         | 
| 40 | 
            +
             
         | 
    
        data/VERSION
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            0.2.3
         | 
    
        data/bin/vmdeploy
    ADDED
    
    | @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            $: << File.join(File.dirname(__FILE__),'..','lib')
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'deploy'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            def usage(errormessage = "")
         | 
| 7 | 
            +
              puts errormessage
         | 
| 8 | 
            +
              puts "usage: "
         | 
| 9 | 
            +
              puts "   vmdeploy <archive> [executable]" 
         | 
| 10 | 
            +
              puts ""
         | 
| 11 | 
            +
              puts "   archive   : the archive to deploy"
         | 
| 12 | 
            +
              puts "   executable: the executable to execute after deployment - default : 'vmsim'" 
         | 
| 13 | 
            +
              puts ""
         | 
| 14 | 
            +
              exit 1
         | 
| 15 | 
            +
            end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            archive = ARGV[0]
         | 
| 18 | 
            +
            executable = ARGV[1] || 'vmsim' 
         | 
| 19 | 
            +
            usage unless archive
         | 
| 20 | 
            +
            usage("archive #{archive} does not exist") unless File.exists?(archive)
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            Deploy::Deployer.new(executable).deploy ARGV[0]
         | 
| 23 | 
            +
             | 
    
        data/bin/vmsim
    ADDED
    
    | @@ -0,0 +1,37 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            $: << File.expand_path(File.join(File.dirname(__FILE__),'..','lib'))
         | 
| 4 | 
            +
            require 'fileutils'
         | 
| 5 | 
            +
            require 'hardware_adapter'
         | 
| 6 | 
            +
            require 'hardware'
         | 
| 7 | 
            +
            require 'gui'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            simulator = Hardware::Simulator.new
         | 
| 10 | 
            +
            simulator.assemble_hardware
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            simulator_gui = Gui::SimulatorGui.new(simulator)
         | 
| 13 | 
            +
            simulator_gui.show
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            simulator.on_boot do
         | 
| 16 | 
            +
              simulator.drawer(0).fill(Hardware::Can.cola, 2, 0)
         | 
| 17 | 
            +
              simulator.drawer(1).fill(Hardware::Can.fanta, 2, 0)
         | 
| 18 | 
            +
              simulator.drawer(2).fill(Hardware::Can.sprite, 2, 0)
         | 
| 19 | 
            +
              simulator.drawer(3).fill(Hardware::Can.sisi, 2, 0)
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              simulator.cash_register.fill(Hardware::Coin.two_euro, 3, 0)
         | 
| 22 | 
            +
              simulator.cash_register.fill(Hardware::Coin.one_euro, 3, 0)
         | 
| 23 | 
            +
              simulator.cash_register.fill(Hardware::Coin.fifty_cents, 3, 0)
         | 
| 24 | 
            +
            end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            FileUtils.cd(File.expand_path('~/.vendingmachine/control')) do
         | 
| 27 | 
            +
              $: << 'lib'
         | 
| 28 | 
            +
              load 'main.rb'
         | 
| 29 | 
            +
              Control.main if defined?(Control.main)
         | 
| 30 | 
            +
            end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            simulator.boot
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            while !simulator_gui.stopped? do
         | 
| 35 | 
            +
              Gtk.main_iteration while Gtk.events_pending?
         | 
| 36 | 
            +
            end
         | 
| 37 | 
            +
            puts "vending machine going home drunk"
         | 
| @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            actuators
         | 
| 2 | 
            +
            :cash_drop_100
         | 
| 3 | 
            +
            :cash_drop_200
         | 
| 4 | 
            +
            :cash_drop_50
         | 
| 5 | 
            +
            :display_show, <line>, <message>
         | 
| 6 | 
            +
            :drawer_drop_can_0
         | 
| 7 | 
            +
            :drawer_drop_can_1
         | 
| 8 | 
            +
            :drawer_drop_can_2
         | 
| 9 | 
            +
            :drawer_drop_can_3
         | 
| 10 | 
            +
             | 
| 11 | 
            +
             | 
| 12 | 
            +
            sensors
         | 
| 13 | 
            +
            :bin_entry
         | 
| 14 | 
            +
            :bin_fetch_all
         | 
| 15 | 
            +
            :button_press_0
         | 
| 16 | 
            +
            :button_press_1
         | 
| 17 | 
            +
            :button_press_2
         | 
| 18 | 
            +
            :button_press_3
         | 
| 19 | 
            +
            :cash_fill_100
         | 
| 20 | 
            +
            :cash_fill_200
         | 
| 21 | 
            +
            :cash_fill_50
         | 
| 22 | 
            +
            :cash_insert_100
         | 
| 23 | 
            +
            :cash_insert_200
         | 
| 24 | 
            +
            :cash_insert_50
         | 
| 25 | 
            +
            :drawer_drop_can_0
         | 
| 26 | 
            +
            :drawer_drop_can_1
         | 
| 27 | 
            +
            :drawer_drop_can_2
         | 
| 28 | 
            +
            :drawer_drop_can_3
         | 
| 29 | 
            +
            :drawer_fill_can_0
         | 
| 30 | 
            +
            :drawer_fill_can_1
         | 
| 31 | 
            +
            :drawer_fill_can_2
         | 
| 32 | 
            +
            :drawer_fill_can_3
         | 
    
        data/doc/handout.odt
    ADDED
    
    | Binary file | 
    
        data/doc/machine.jpg
    ADDED
    
    | Binary file | 
    
        data/doc/todo.txt
    ADDED
    
    | @@ -0,0 +1,25 @@ | |
| 1 | 
            +
             - vmsim start - required in ~/.vendingmachine/control/main.rb
         | 
| 2 | 
            +
             - zut in gem
         | 
| 3 | 
            +
             - gem voor 
         | 
| 4 | 
            +
                - api voor display (show (line, message))
         | 
| 5 | 
            +
                    Devices.display_show(line, message)
         | 
| 6 | 
            +
                    Devices.on_button_pressed(0) { } 
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                    Devices.get_display().show(line, message)
         | 
| 9 | 
            +
                        Actuators(:display).fire(:show, line, message)
         | 
| 10 | 
            +
                - api voor buttons (list, on_pres(0))
         | 
| 11 | 
            +
                    VM.get_button(0).on_press { }
         | 
| 12 | 
            +
                        Sensors.on(
         | 
| 13 | 
            +
                - api voor drawers (on_pres(0), drop(0))
         | 
| 14 | 
            +
                    VM.get_drawer(0).drop()
         | 
| 15 | 
            +
                - api voor bin (on_entry, on_fetch_all)
         | 
| 16 | 
            +
                - api voor cashregister (on_fill(100), on_insert(100), drop(100))
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            ============== DONEDONEDONE ====================
         | 
| 19 | 
            +
             - vmdeploy exe
         | 
| 20 | 
            +
             - vmdeploy 
         | 
| 21 | 
            +
                - stopt vmsim als running
         | 
| 22 | 
            +
                - 
         | 
| 23 | 
            +
                - start vmsim
         | 
| 24 | 
            +
             | 
| 25 | 
            +
             | 
| @@ -0,0 +1,61 @@ | |
| 1 | 
            +
            module Adapter
         | 
| 2 | 
            +
              class NoActuatorBlockGiven < Exception 
         | 
| 3 | 
            +
              end
         | 
| 4 | 
            +
              class DuplicateActuatorException < Exception 
         | 
| 5 | 
            +
              end
         | 
| 6 | 
            +
              class NoSuchActuatorException < Exception 
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              class AsynchronousFirer 
         | 
| 10 | 
            +
                attr_reader :block
         | 
| 11 | 
            +
                protected :block
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def initialize(&block)
         | 
| 14 | 
            +
                  @block = block
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def fire(*params)
         | 
| 18 | 
            +
                  Thread.start { block.call(*params) }
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              class SynchronousFirer < AsynchronousFirer
         | 
| 23 | 
            +
                def fire(*params)
         | 
| 24 | 
            +
                  block.call *params
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
             | 
| 29 | 
            +
              class ActuatorCollection
         | 
| 30 | 
            +
                class Null
         | 
| 31 | 
            +
                  def create_actuator_for(actuator_name, &block) 
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                  def fire(actuator_name)
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def self.null
         | 
| 38 | 
            +
                  Null.new
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def initialize(firer_class = AsynchronousFirer)
         | 
| 42 | 
            +
                  @actuators = {}
         | 
| 43 | 
            +
                  @firer_class = firer_class
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def list
         | 
| 47 | 
            +
                  @actuators.entries.collect {|e| e[0].to_s}.sort.collect{|e| e.to_sym}
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                def create_actuator_for(actuator_name, &block) 
         | 
| 51 | 
            +
                  raise NoActuatorBlockGiven.new("must supply a code block for an actuator - none given") unless block_given?
         | 
| 52 | 
            +
                  raise DuplicateActuatorException.new("actuator :#{actuator_name} already exists") if @actuators.has_key?(actuator_name)
         | 
| 53 | 
            +
                  @actuators[actuator_name.to_sym] = @firer_class.new(&block)
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                def fire(actuator_name, *params)
         | 
| 57 | 
            +
                  raise NoSuchActuatorException.new("actuator :#{actuator_name} does not exist") unless @actuators.has_key?(actuator_name)
         | 
| 58 | 
            +
                  @actuators[actuator_name.to_sym].fire(*params)
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
            end
         | 
| @@ -0,0 +1,57 @@ | |
| 1 | 
            +
            module Adapter
         | 
| 2 | 
            +
              class Sensor
         | 
| 3 | 
            +
                def fire
         | 
| 4 | 
            +
                  @on_fire_block.call if @on_fire_block
         | 
| 5 | 
            +
                end
         | 
| 6 | 
            +
                def on_fire_do(&block)
         | 
| 7 | 
            +
                  @on_fire_block = block
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
                def remove_fire_listener
         | 
| 10 | 
            +
                  @on_fire_block = nil
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
              
         | 
| 14 | 
            +
              class NoSuchSensorException < Exception 
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
              
         | 
| 17 | 
            +
              class SensorCollection
         | 
| 18 | 
            +
                class Null
         | 
| 19 | 
            +
                  def on(sensor)
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                  def create_sensor_for(event)
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                  def fire(sensor)
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
                
         | 
| 27 | 
            +
                def initialize
         | 
| 28 | 
            +
                  @sensors = {}
         | 
| 29 | 
            +
                  @sensors.default = Sensor.new
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
                
         | 
| 32 | 
            +
                def self.null
         | 
| 33 | 
            +
                  Null.new 
         | 
| 34 | 
            +
                end 
         | 
| 35 | 
            +
                
         | 
| 36 | 
            +
                def list
         | 
| 37 | 
            +
                  @sensors.entries.collect { |e| e[0].to_s }.sort.collect{ |e| e.to_sym }
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
                
         | 
| 40 | 
            +
                def on(sensor, &block)
         | 
| 41 | 
            +
                  raise NoSuchSensorException.new("sensor :#{sensor} does not exist") unless @sensors.has_key?(sensor.to_sym)
         | 
| 42 | 
            +
                  @sensors[sensor.to_sym].on_fire_do(&block) 
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
                
         | 
| 45 | 
            +
                def create_sensor_for(event)
         | 
| 46 | 
            +
                  @sensors[event.to_sym] = Sensor.new    
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
                
         | 
| 49 | 
            +
                def fire(sensor)
         | 
| 50 | 
            +
                  @sensors[sensor.to_sym].fire
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def remove_fire_listeners
         | 
| 54 | 
            +
                  @sensors.collect {|name, sensor| sensor.remove_fire_listener } 
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
            end
         | 
| @@ -0,0 +1,64 @@ | |
| 1 | 
            +
            require 'tmpdir'
         | 
| 2 | 
            +
            module ArchiveBuilder
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              def an_archive(filename, work_dir = Dir.tmpdir, &block)
         | 
| 5 | 
            +
                archive = ArchiveBuilder.new(filename, work_dir)
         | 
| 6 | 
            +
                archive.in_work_dir(&block) if block_given?
         | 
| 7 | 
            +
                return archive.build.file_path
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              require 'fileutils'
         | 
| 11 | 
            +
              class ArchiveBuilder
         | 
| 12 | 
            +
                include FileUtils
         | 
| 13 | 
            +
                def initialize(filename, work_dir = Dir.tmpdir)
         | 
| 14 | 
            +
                  @filename = filename
         | 
| 15 | 
            +
                  @work_dir = File.join(work_dir, 'archive_builder')
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def a_file(filename)
         | 
| 19 | 
            +
                  FileBuilder.new(filename)
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def in_work_dir(&block)
         | 
| 23 | 
            +
                  mk_work_dir
         | 
| 24 | 
            +
                  cd(@work_dir) do
         | 
| 25 | 
            +
                    instance_eval(&block)
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def build
         | 
| 30 | 
            +
                  cd(@work_dir) do
         | 
| 31 | 
            +
                    system "zip -r #{@filename} * >> /dev/null"
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                  return self
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                def file_path
         | 
| 37 | 
            +
                  File.join(@work_dir, @filename)
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
                private
         | 
| 40 | 
            +
                def mk_work_dir
         | 
| 41 | 
            +
                  rm_r(@work_dir) if File.exists?(@work_dir) && @work_dir =~ /^\/tmp/
         | 
| 42 | 
            +
                  mkdir_p(@work_dir) unless File.exists?(@work_dir)
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              class FileBuilder
         | 
| 47 | 
            +
                include FileUtils
         | 
| 48 | 
            +
                attr_reader :filename
         | 
| 49 | 
            +
                def initialize(filename)
         | 
| 50 | 
            +
                  @filename = filename
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def with(content)
         | 
| 54 | 
            +
                  create_dirs
         | 
| 55 | 
            +
                  File.open(filename, "w+") { |f| f.puts content }
         | 
| 56 | 
            +
                  return self
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
                def create_dirs
         | 
| 59 | 
            +
                  dirname = File.dirname(filename)
         | 
| 60 | 
            +
                  mkdir_p dirname unless File.exists?(dirname) 
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
            end
         | 
| 64 | 
            +
             | 
    
        data/lib/control/main.rb
    ADDED
    
    | @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            require "vmlog"
         | 
| 2 | 
            +
            require 'devices'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            include VMLog
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Control
         | 
| 7 | 
            +
              def self.main
         | 
| 8 | 
            +
                # here, you should load your vending machine control software
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                # as an example of using the actuators and sensors (see doc/actuators_and_sensors)
         | 
| 11 | 
            +
                Devices.display_show(0, 'Choose')
         | 
| 12 | 
            +
                Devices.display_show(1, 'Please...')
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                Devices.on_button_pressed(0) do
         | 
| 15 | 
            +
                  log "cola pressed"
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  Devices.display_show(0, 'Cola')
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
            end
         | 
| 21 | 
            +
             | 
    
        data/lib/control.rb
    ADDED
    
    
| @@ -0,0 +1,96 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            require 'fileutils'
         | 
| 3 | 
            +
            module Deploy
         | 
| 4 | 
            +
              def self.deploy(file)
         | 
| 5 | 
            +
                Deployer.new(file).deploy
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
              class Deployer
         | 
| 8 | 
            +
                include FileUtils
         | 
| 9 | 
            +
                DEPLOYED_DIR = File.expand_path("~/.vendingmachine/control")
         | 
| 10 | 
            +
                PIDFILE = File.expand_path("~/.vendingmachine/pid")
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                attr_reader :executable, :current_pid
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def initialize(executable)
         | 
| 15 | 
            +
                  @executable = executable
         | 
| 16 | 
            +
                  @log = ""
         | 
| 17 | 
            +
                  $: << DEPLOYED_DIR unless $:.include?(DEPLOYED_DIR)
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def run_log
         | 
| 21 | 
            +
                  @log
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def deploy(file_path)
         | 
| 25 | 
            +
                  create_deployment_environment
         | 
| 26 | 
            +
                  read_current_pid
         | 
| 27 | 
            +
                  kill_running_process
         | 
| 28 | 
            +
                  install_deployed_code(file_path)
         | 
| 29 | 
            +
                  start_deployed_code
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def kill
         | 
| 33 | 
            +
                  read_current_pid
         | 
| 34 | 
            +
                  kill_running_process
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def wait_for
         | 
| 38 | 
            +
                  @running_thread.join if @running_thread
         | 
| 39 | 
            +
                  self
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                private
         | 
| 43 | 
            +
                def start_deployed_code
         | 
| 44 | 
            +
                  @running_thread = Thread.start do 
         | 
| 45 | 
            +
                    IO.popen(executable) do |io|
         | 
| 46 | 
            +
                      save_current_pid io.pid
         | 
| 47 | 
            +
                      while !io.eof? do
         | 
| 48 | 
            +
                        @log << io.gets
         | 
| 49 | 
            +
                      end
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
                    clear_current_pid
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                def install_deployed_code(file_path)
         | 
| 56 | 
            +
                  rm_r Dir.glob(File.join(DEPLOYED_DIR, '*'))
         | 
| 57 | 
            +
                  file_path = File.expand_path(file_path)
         | 
| 58 | 
            +
                  cd(DEPLOYED_DIR) do
         | 
| 59 | 
            +
                    sh "unzip #{file_path}"
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def kill_running_process
         | 
| 64 | 
            +
                  begin
         | 
| 65 | 
            +
                    p current_pid
         | 
| 66 | 
            +
                    Process.kill("SIGKILL", current_pid) if current_pid
         | 
| 67 | 
            +
                  rescue Errno::ESRCH => e
         | 
| 68 | 
            +
                    # thats fine
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                def save_current_pid(pid)
         | 
| 73 | 
            +
                  @current_pid = pid
         | 
| 74 | 
            +
                  File.open(PIDFILE, 'w+') { |f| f.write(@current_pid.to_s) }
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                def read_current_pid
         | 
| 78 | 
            +
                  @current_pid = File.read(PIDFILE).to_i if File.exists?(PIDFILE)
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                def clear_current_pid
         | 
| 82 | 
            +
                  @current_pid = nil
         | 
| 83 | 
            +
                  rm PIDFILE if File.exists?(PIDFILE)
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                def create_deployment_environment
         | 
| 87 | 
            +
                  mkdir_p DEPLOYED_DIR unless File.exists?(DEPLOYED_DIR)
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                def sh(command)
         | 
| 91 | 
            +
                  system "#{command} >> /dev/null"
         | 
| 92 | 
            +
                end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
              end
         | 
| 95 | 
            +
            end
         | 
| 96 | 
            +
             | 
    
        data/lib/deploy.rb
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            require 'deploy/deployer'
         | 
| @@ -0,0 +1,73 @@ | |
| 1 | 
            +
            require 'hardware_adapter'
         | 
| 2 | 
            +
            module Devices
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              def self.method_missing(method_sym, *arguments, &block)
         | 
| 5 | 
            +
                devices = Devices.new
         | 
| 6 | 
            +
                devices.send(method_sym, *arguments, &block)
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              class Devices
         | 
| 10 | 
            +
                def initialize(actuators = Adapter.actuators, sensors = Adapter.sensors)
         | 
| 11 | 
            +
                  @actuators = actuators
         | 
| 12 | 
            +
                  @sensors = sensors
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                # display a a message on the specified line on the display
         | 
| 16 | 
            +
                # the line is a 0 based index in the available lines on the display
         | 
| 17 | 
            +
                def display_show(line, message)
         | 
| 18 | 
            +
                  actuators.fire(:display_show, line, message)
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                # drops a coin in the bin where:
         | 
| 22 | 
            +
                # coin is 50 100 or 200 cts
         | 
| 23 | 
            +
                def cash_drop_coin(coin)
         | 
| 24 | 
            +
                  actuators.fire(:"cash_drop_#{coin}")
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                # register a handler on filling coin of a specific 
         | 
| 28 | 
            +
                # kind in the cash register
         | 
| 29 | 
            +
                def on_cash_filled(coin, &block) 
         | 
| 30 | 
            +
                  sensors.on(:"cash_fill_#{coin}", &block)
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                # register a handler on inserting a specifica coin 
         | 
| 34 | 
            +
                # in the cash register as part of paying
         | 
| 35 | 
            +
                def on_cash_inserted(coin, &block) 
         | 
| 36 | 
            +
                  sensors.on(:"cash_insert_#{coin}", &block)
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                # register a handler on getting something being dropped in the bin
         | 
| 40 | 
            +
                def on_bin_entry(&block)
         | 
| 41 | 
            +
                  sensors.on(:bin_entry, &block)
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                # register a handler on sbdy getting stuff from the bin
         | 
| 45 | 
            +
                def on_bin_fetch_all(&block)
         | 
| 46 | 
            +
                  sensors.on(:bin_fetch_all, &block)
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                # register a handleer on sbgy pressing a specific button 
         | 
| 50 | 
            +
                # where button can be one of 0 1 2 or three
         | 
| 51 | 
            +
                def on_button_pressed(button, &block)
         | 
| 52 | 
            +
                  sensors.on(:"button_press_#{button}", &block)
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                # drops a can from a specified drawer
         | 
| 56 | 
            +
                def drawer_drop_can(drawer)
         | 
| 57 | 
            +
                  actuators.fire(:"drawer_drop_can_#{drawer}")
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                # register a handler on a drawer dropping a can
         | 
| 61 | 
            +
                def on_drawer_dropped(drawer, &block)
         | 
| 62 | 
            +
                  sensors.on(:"drawer_drop_can_#{drawer}", &block)
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                # register a handler on a drawer being filled with a can
         | 
| 66 | 
            +
                def on_drawer_filled(drawer, &block)
         | 
| 67 | 
            +
                  sensors.on(:"drawer_fill_can_#{drawer}", &block)
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                private 
         | 
| 71 | 
            +
                attr_reader :actuators, :sensors
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
            end
         | 
    
        data/lib/devices.rb
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            require 'devices/devices'
         |