enginevib 0.1.0

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.
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