enginevib 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +12 -0
  3. data/.gitignore +9 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +82 -0
  8. data/Rakefile +17 -0
  9. data/bin/bundler +16 -0
  10. data/bin/console +14 -0
  11. data/bin/enginevib +16 -0
  12. data/bin/rake +16 -0
  13. data/bin/rake-compiler +16 -0
  14. data/bin/rubocop +16 -0
  15. data/bin/ruby-parse +16 -0
  16. data/bin/ruby-rewrite +16 -0
  17. data/bin/setup +7 -0
  18. data/dtrace/dtrace_rtos_classes.png +0 -0
  19. data/dtrace/dtrace_rtos_classes_no_str.png +0 -0
  20. data/dtrace/dtrace_rtos_syscalls.png +0 -0
  21. data/dtrace/sample_output_rtos_classes.csv +85 -0
  22. data/dtrace/sample_output_rtos_syscall.csv +53 -0
  23. data/dtrace/sample_output_soft_classes.csv +86 -0
  24. data/dtrace/sample_output_soft_syscall.csv +142 -0
  25. data/dtrace/script.d +14 -0
  26. data/enginevib.gemspec +30 -0
  27. data/exe/enginevib +20 -0
  28. data/exe/openmic +9 -0
  29. data/ext/rtos/extconf.rb +3 -0
  30. data/ext/rtos/rtos.c +32 -0
  31. data/lib/enginevib/averager.rb +27 -0
  32. data/lib/enginevib/controller.rb +9 -0
  33. data/lib/enginevib/io.rb +13 -0
  34. data/lib/enginevib/launcher.rb +50 -0
  35. data/lib/enginevib/main.rb +40 -0
  36. data/lib/enginevib/output.rb +55 -0
  37. data/lib/enginevib/scheduler.rb +70 -0
  38. data/lib/enginevib/sensor.rb +26 -0
  39. data/lib/enginevib/soft_ticker.rb +12 -0
  40. data/lib/enginevib/stats.rb +13 -0
  41. data/lib/enginevib/system.rb +15 -0
  42. data/lib/enginevib/version.rb +4 -0
  43. data/lib/enginevib.rb +7 -0
  44. data/lib/rtos.bundle +0 -0
  45. data/test/averager_test.rb +16 -0
  46. data/test/controller_test.rb +8 -0
  47. data/test/enginevib_test.rb +7 -0
  48. data/test/fixtures/dummy +2 -0
  49. data/test/io_test.rb +10 -0
  50. data/test/main_test.rb +20 -0
  51. data/test/scheduler_test.rb +8 -0
  52. data/test/sensor_test.rb +13 -0
  53. data/test/soft_ticker_test.rb +8 -0
  54. data/test/stats_test.rb +16 -0
  55. data/test/system_test.rb +8 -0
  56. data/test/test_helper.rb +24 -0
  57. metadata +186 -0
data/dtrace/script.d ADDED
@@ -0,0 +1,14 @@
1
+ ruby*:::object-create
2
+ {
3
+ @objects[copyinstr(arg0)] = count();
4
+ }
5
+
6
+ ruby*:::method-entry
7
+ {
8
+ @objects[copyinstr(arg1)] = count();
9
+ }
10
+
11
+ ruby*:::cmethod-entry
12
+ {
13
+ @objects[copyinstr(arg1)] = count();
14
+ }
data/enginevib.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'enginevib/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "enginevib"
8
+ spec.version = Enginevib::VERSION
9
+ spec.authors = ["Eduardo Mourão"]
10
+ spec.email = ["eduardo.a20@gmail.com"]
11
+
12
+ spec.summary = %q{Aircraft Avionics Built in Ruby}
13
+ spec.description = %q{Aircraft Avionics Built in Ruby}
14
+ spec.homepage = "https://github.com/eduardordm/enginevib"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files`.split("\n")
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+ spec.extensions = ["ext/rtos/extconf.rb"]
22
+
23
+ spec.add_dependency "curses"
24
+ spec.add_dependency "rake-compiler"
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.10"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "minitest"
29
+ spec.add_development_dependency "rubocop"
30
+ end
data/exe/enginevib ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'benchmark'
4
+
5
+ require 'enginevib/launcher'
6
+ require 'enginevib/io'
7
+ require 'enginevib/main'
8
+ require 'enginevib/sensor'
9
+ require 'enginevib/output'
10
+ require 'enginevib/system'
11
+ require 'enginevib/soft_ticker'
12
+ require 'enginevib/scheduler'
13
+ require 'enginevib/controller'
14
+ require 'enginevib/stats'
15
+ require 'enginevib/averager'
16
+
17
+
18
+ require 'rtos'
19
+
20
+ Enginevib::Main.new.launch
data/exe/openmic ADDED
@@ -0,0 +1,9 @@
1
+ #!/bin/sh
2
+
3
+ socat -d -d pty,raw,echo=0 pty,raw,echo=0 &
4
+
5
+ sleep 1
6
+
7
+ while true; do rec -n stat trim 0 .001 2>&1 | awk '/^Maximum amplitude/' | cut -d":" -f2 | sed 's/ //g' > /dev/ttys000; done
8
+
9
+
@@ -0,0 +1,3 @@
1
+ require "mkmf"
2
+
3
+ create_makefile "rtos/rtos"
data/ext/rtos/rtos.c ADDED
@@ -0,0 +1,32 @@
1
+ #include <ruby.h>
2
+ #include <unistd.h>
3
+
4
+ static
5
+ VALUE initialize(VALUE self)
6
+ {
7
+ return self;
8
+ }
9
+
10
+ static
11
+ VALUE delay_rt(VALUE c, VALUE t)
12
+ {
13
+ usleep(NUM2DBL(t)*1000000);
14
+ return Qnil;
15
+ }
16
+
17
+ static
18
+ VALUE preempt_rt(VALUE c)
19
+ {
20
+ rb_need_block();
21
+ rb_yield(Qnil);
22
+ return Qnil;
23
+ }
24
+
25
+ void Init_rtos(void)
26
+ {
27
+ VALUE cRtos = rb_define_module("Rtos");
28
+
29
+ rb_define_method(cRtos, "initialize", initialize, 0);
30
+ rb_define_method(cRtos, "preempt_rt", preempt_rt, 0);
31
+ rb_define_method(cRtos, "delay_rt", delay_rt, 1);
32
+ }
@@ -0,0 +1,27 @@
1
+ module Enginevib
2
+ # Defines the Averager class
3
+ class Averager
4
+ AVERAGE_SAMPLE_SIZE = 100
5
+
6
+ def initialize(size = AVERAGE_SAMPLE_SIZE)
7
+ @size = size
8
+ @nums = []
9
+ @sum = 0.0
10
+ end
11
+
12
+ def <<(hello)
13
+ @nums << hello
14
+ goodbye = @nums.length > @size ? @nums.shift : 0
15
+ @sum += hello - goodbye
16
+ self
17
+ end
18
+
19
+ def average
20
+ @sum / @nums.length
21
+ end
22
+
23
+ def to_s
24
+ average.to_s
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ module Enginevib
2
+ # Defines a controller which can be operated by external tools
3
+ class Controller
4
+ attr_accessor :inop
5
+ def initialize
6
+ @inop = false
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ # Non-blocking patch. I'm a terrible person.
2
+ class IO
3
+ def readline_nonblock
4
+ rlnb_buffer = ''
5
+ until (ch = read_nonblock(1)).nil?
6
+ rlnb_buffer << ch
7
+ if ch == "\n"
8
+ result = rlnb_buffer
9
+ return result
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,50 @@
1
+ module Enginevib
2
+ # Defines the Launcher module which must be defined in Main.rb
3
+ module Launcher
4
+ attr_accessor :options
5
+
6
+ def launch
7
+ start_scheduler
8
+ rescue SystemExit, Interrupt
9
+ puts 'Good bye!' # May not be printed since we are using curses.
10
+ end
11
+
12
+ def parse_arguments
13
+ @options = {
14
+ simulation_mode: true,
15
+ serial_port: nil
16
+ }
17
+ OptionParser.new do |opts|
18
+ opts.banner = 'Usage: enginevib [options]'
19
+ on_simulation_mode(opts)
20
+ on_serial_port(opts)
21
+ on_help(opts)
22
+ end.parse!
23
+ end
24
+
25
+ private
26
+
27
+ def on_simulation_mode(opts)
28
+ opts.on('-s',
29
+ '--simulation-mode',
30
+ 'Enables the simulation mode and ignores --serial-port') do |s|
31
+ @options[:simulation_mode] = s
32
+ end
33
+ end
34
+
35
+ def on_serial_port(opts)
36
+ opts.on('-pSERIAL_PORT',
37
+ '--serial-port=SERIAL_PORT',
38
+ 'Serial device to read data from. Example: /dev/ttys005') do |p|
39
+ @options[:serial_port] = p
40
+ end
41
+ end
42
+
43
+ def on_help(opts)
44
+ opts.on('-h', '--help', 'Prints this help') do
45
+ puts opts
46
+ exit
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,40 @@
1
+ module Enginevib
2
+ # The main class. run is called when run from command line.
3
+ class Main
4
+ include Launcher
5
+ attr_accessor :scheduler, :sensor, :output, :system, :controller
6
+
7
+ DEADLINE = 30.0 / 1000.0 # 30 milliseconds
8
+ TOLERANCE = 7.0 / 1000.0 # 7 miliseconds
9
+ AVERAGE_SAMPLE_SIZE = 100 # 100 ticks
10
+
11
+ def initialize
12
+ parse_arguments
13
+ initialize_components
14
+ initialize_scheduler
15
+ end
16
+
17
+ def initialize_components
18
+ @sensor = Sensor.new(@options[:serial_port], @options[:simulation_mode])
19
+ @output = Output.new
20
+ @system = System.new
21
+ @controller = Controller.new
22
+ end
23
+
24
+ def initialize_scheduler
25
+ @scheduler = Scheduler.new(DEADLINE, TOLERANCE) do |stats|
26
+ if @controller.inop
27
+ sleep DEADLINE - TOLERANCE
28
+ else
29
+ data = @sensor.read
30
+ computed_data = @system.compute(data)
31
+ @output.refresh_display(computed_data, stats)
32
+ end
33
+ end
34
+ end
35
+
36
+ def start_scheduler
37
+ @scheduler.start
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,55 @@
1
+ require 'curses'
2
+
3
+ module Enginevib
4
+ # Defines an output class
5
+ class Output
6
+ attr_accessor :screen, :window
7
+
8
+ attr_writer :warning
9
+
10
+ def initialize
11
+ return if $TEST
12
+ init_renderer
13
+ end
14
+
15
+ def refresh_display(data, stats)
16
+ return if $TEST
17
+ render_message data, stats
18
+ end
19
+
20
+ private
21
+
22
+ def init_renderer
23
+ setup_curses
24
+ @window = Curses::Window.new(8, 23,
25
+ (Curses.lines - 8) / 2,
26
+ (Curses.cols - 23) / 2)
27
+ end
28
+
29
+ def setup_curses
30
+ Curses.tap do |c|
31
+ c.init_screen
32
+ c.nonl
33
+ c.cbreak
34
+ c.noecho
35
+ c.curs_set(0)
36
+ @screen = c.stdscr
37
+ end
38
+ @screen.scrollok(false)
39
+ end
40
+
41
+ def render_message(data, stats)
42
+ @window.tap do |w|
43
+ w.setpos(2, 3)
44
+ w.addstr(data.to_s)
45
+ w.setpos(4, 3)
46
+
47
+ wnd = stats.window_avg
48
+ st = stats.warning ? 'WARN' : 'OP '
49
+
50
+ w.addstr(">>#{st}/#{stats.memory}/#{wnd} ms ")
51
+ w.refresh
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,70 @@
1
+ require 'rtos'
2
+
3
+ module Enginevib
4
+ # Defines the scheduler class
5
+ class Scheduler
6
+ include $SOFT_TICKER ? SoftTicker : Rtos
7
+
8
+ attr_accessor :deadline, :block, :tolerance, :stats, :averager, :inop
9
+
10
+ def initialize(deadline, tolerance, &block)
11
+ @deadline = deadline
12
+ @block = block
13
+ @tolerance = tolerance
14
+ @stats = Stats.new
15
+ @averager = Averager.new
16
+ end
17
+
18
+ def start
19
+ preempt_rt { loop { tick } }
20
+ end
21
+
22
+ def tick
23
+ execution_time = execute_block
24
+
25
+ actual_wait_time = wait_for_next_tick(execution_time)
26
+
27
+ measure_time = measure_execution(execution_time, actual_wait_time)
28
+
29
+ verify_measure_time(execution_time, actual_wait_time, measure_time)
30
+ end
31
+
32
+ private
33
+
34
+ def execute_block
35
+ Benchmark.realtime do
36
+ @block.call @stats
37
+ GC.start
38
+ end
39
+ end
40
+
41
+ def wait_for_next_tick(execution_time)
42
+ Benchmark.realtime do
43
+ delay_rt([@deadline - execution_time - tolerance, 0].max)
44
+ end
45
+ end
46
+
47
+ def measure_execution(execution_time, actual_wait_time)
48
+ Benchmark.realtime do
49
+ @stats.tap do |s|
50
+ s.memory =
51
+ GC.stat(:total_allocated_objects) - GC.stat(:total_freed_objects)
52
+ s.window = execution_time + actual_wait_time
53
+ s.warning = @stats.window > @deadline
54
+ measure_execution_avg
55
+ end
56
+ end
57
+ end
58
+
59
+ def measure_execution_avg
60
+ @averager << @stats.window * 1000
61
+ @stats.window_avg = @averager.average.to_i
62
+ end
63
+
64
+ def verify_measure_time(execution_time, actual_wait_time, measure_time)
65
+ @stats.warning =
66
+ (execution_time + actual_wait_time + measure_time) > @deadline
67
+ !@stats.warning
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,26 @@
1
+ module Enginevib
2
+ # Defines a sensor class which can read data from virtual ports, non-blocking.
3
+ class Sensor
4
+ attr_accessor :fd, :io, :buffered_data, :simulation_mode
5
+
6
+ def initialize(serial_port, simulation_mode)
7
+ @simulation_mode = simulation_mode
8
+ init_io(serial_port)
9
+ end
10
+
11
+ def read
12
+ @buffered_data =
13
+ @simulation_mode ? rand(0.10..1.00) : @io.readline_nonblock.to_f
14
+ rescue
15
+ @buffered_data
16
+ end
17
+
18
+ private
19
+
20
+ def init_io(serial_port)
21
+ return if @simulation_mode
22
+ @fd = IO.sysopen(serial_port, 'r')
23
+ @io = IO.new(fd, 'r')
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ module Enginevib
2
+ # Defines the soft ticker methods as a replacement for Rtos
3
+ module SoftTicker
4
+ def preempt_rt
5
+ yield
6
+ end
7
+
8
+ def delay_rt(t_seconds)
9
+ sleep(t_seconds)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ module Enginevib
2
+ # A PORO. Should be faster than struct and hash.
3
+ class Stats
4
+ attr_accessor :warning, :window, :memory, :window_avg
5
+
6
+ def initialize
7
+ @warning = false
8
+ @window = 0
9
+ @memory = 0
10
+ @window_avg = 0
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module Enginevib
2
+ # Defines the system class
3
+ class System
4
+ attr_accessor :averager
5
+
6
+ def initialize
7
+ @averager = Averager.new
8
+ end
9
+
10
+ def compute(data)
11
+ @averager << data
12
+ format('%2.1f', (@averager.average * 10.0))
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ # Version definition
2
+ module Enginevib
3
+ VERSION = '0.1.0'
4
+ end
data/lib/enginevib.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'enginevib/version'
2
+
3
+ # Enginevib module
4
+ module Enginevib
5
+ end
6
+
7
+ require 'rtos/rtos'
data/lib/rtos.bundle ADDED
Binary file
@@ -0,0 +1,16 @@
1
+ require 'test_helper'
2
+
3
+ class AveragerTest < Minitest::Test
4
+ def test_average_must_be_correct
5
+ avg = Enginevib::Averager.new(10)
6
+
7
+ avg << 15
8
+ avg << 5
9
+
10
+ assert_equal 10, avg.average
11
+
12
+ avg << 40
13
+
14
+ assert_equal 20, avg.average
15
+ end
16
+ end
@@ -0,0 +1,8 @@
1
+ require 'test_helper'
2
+
3
+ class ControllerTest < Minitest::Test
4
+ def test_must_be_always_on
5
+ controller = Enginevib::Controller.new
6
+ assert !controller.inop
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class EnginevibTest < Minitest::Test
4
+ def test_that_it_has_a_version_number
5
+ refute_nil ::Enginevib::VERSION
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ 1234
2
+ 1234
data/test/io_test.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'test_helper'
2
+
3
+ class IoTest < Minitest::Test
4
+ def test_must_add_readline_nonblock_feature
5
+ fd = IO.sysopen("test/fixtures/dummy", 'r')
6
+ io = IO.new(fd, 'r')
7
+
8
+ assert io.respond_to? :readline_nonblock
9
+ end
10
+ end
data/test/main_test.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'test_helper'
2
+
3
+ class MainTest < Minitest::Test
4
+ def test_must_initialize_components
5
+ main = Enginevib::Main.new
6
+ refute_nil main.controller
7
+ refute_nil main.sensor
8
+ refute_nil main.output
9
+ end
10
+
11
+ def test_must_initialize_scheduler
12
+ main = Enginevib::Main.new
13
+ refute_nil main.scheduler
14
+ end
15
+
16
+ def test_deadline_must_be_met
17
+ main = Enginevib::Main.new
18
+ (1..100).each { assert main.scheduler.tick }
19
+ end
20
+ end
@@ -0,0 +1,8 @@
1
+ require 'test_helper'
2
+
3
+ class SchedulerTest < Minitest::Test
4
+ def test_deadline_must_be_met
5
+ main = Enginevib::Main.new
6
+ (1..100).each { assert main.scheduler.tick }
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ require 'test_helper'
2
+
3
+ class SensorTest < Minitest::Test
4
+ def test_must_run_simulation
5
+ sensor = Enginevib::Sensor.new(nil, true)
6
+ refute_nil sensor.read
7
+ end
8
+
9
+ def test_must_read_device
10
+ sensor = Enginevib::Sensor.new("test/fixtures/dummy", false)
11
+ assert_equal 1234.0, sensor.read
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ require 'test_helper'
2
+
3
+ class SofTickerTest < Minitest::Test
4
+ def test_must_comply_with_contract
5
+ Enginevib::SoftTicker.respond_to? :preempt_rt
6
+ Enginevib::SoftTicker.respond_to? :delay_rt
7
+ end
8
+ end
@@ -0,0 +1,16 @@
1
+ require 'test_helper'
2
+
3
+ class StatsTest < Minitest::Test
4
+ def test_must_initialize
5
+ stats = Enginevib::Stats.new
6
+ refute_nil stats
7
+ end
8
+
9
+ def test_must_initialize_with_default_values
10
+ stats = Enginevib::Stats.new
11
+ assert_equal false, stats.warning
12
+ assert_equal 0, stats.window
13
+ assert_equal 0, stats.memory
14
+ assert_equal 0, stats.window_avg
15
+ end
16
+ end
@@ -0,0 +1,8 @@
1
+ require 'test_helper'
2
+
3
+ class SystemTest < Minitest::Test
4
+ def test_computed_data_must_be_computed
5
+ sys = Enginevib::System.new
6
+ assert_equal '1.2', sys.compute(0.12)
7
+ end
8
+ end
@@ -0,0 +1,24 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'benchmark'
3
+
4
+ require 'enginevib/launcher'
5
+ require 'enginevib/io'
6
+ require 'enginevib/main'
7
+ require 'enginevib/sensor'
8
+ require 'enginevib/output'
9
+ require 'enginevib/system'
10
+ require 'enginevib/soft_ticker'
11
+ require 'enginevib/scheduler'
12
+ require 'enginevib/controller'
13
+ require 'enginevib/stats'
14
+ require 'enginevib/averager'
15
+ require 'enginevib/version'
16
+
17
+
18
+ require 'rtos'
19
+
20
+ require 'minitest/autorun'
21
+
22
+ $VERBOSE = true
23
+ $SOFT_TICKER = false
24
+ $TEST = true