rsmp-nordic / rsmp

Ruby gem for handling RoadSide Message Protocol (RMSP) communication
MIT License
1 stars 1 forks source link

rsmp

This is a Ruby implementation of the RSMP protocol, including:

Installation

You need a recent version of Ruby intalled. 2.6.3 or later is recommended.

Install required gems:

$ gem install bundler
$ bundle

Usage

Site and Supervisor

The RSMP::Site and RSMP::Supervisor classes can be used to run a RSMP site.

require 'rsmp'
RSMP::Site.new.start        # run site until Ctlr-C is pressed
require 'rsmp'
RSMP::Supervisor.new.start          # run supervisor until Ctlr-C is pressed

Be default, a site will try to connect to a single supervisor on localhost 127.0.0.1, port 12111. By default, a supervisor will listen for sites on port 12111 and accept any site.

You can pass options to control ip adresseses, ports, logging and other behaviour:

require 'rsmp'
settings = {
  'site_id' => 'RN+SI0001',         # site id
  'supervisors' => [                        # list of supervisor to connect to
    { 'ip' => '127.0.0.1', 'port' => 12111 }        # ip and port
  ],
  'log' => {
    'json' => true,     # show raw json messages in log
  }
}
RSMP::Site.new(site_settings:settings)

See lib/rsmp/site.rb and lib/rsmp/supervisor.rb for a list of settings and their default values.

Concurrency

The async and async-io gems are used to handle concurrency. Everything happens in a single thread, but fibers are used to switch between tasks whenever an IO operation blocks.

If you start a site or a supervisor inside an Async block, it will run concurrently:

require 'rsmp'
settings = {
  'log' => { 'active' => false }    # disable log output
}
Async do |task|
  site = RSMP::Site.new(site_settings:settings)
  site.start                    # run concurrently since we're inside an Async block
  loop do
    task.sleep 1            # use task.sleep() instead of sleep() to avoid blocking
    puts "Latest archive item: #{site.archive.items.last}"
  end
end

Use task.show_hierarchy to see what task are created, task.stop() to stop all sites and supervisors running inside it:

require 'rsmp'
Async do |task|
  RSMP::Site.new.start
  task.sleep 1
  task.print_hierarchy
  task.stop     # stop everything inside this Async block
end
puts "Bye!"

RSMP::Site and RSMP::Supervisor is not Async task themselves, but each contain a task attribute containing an async task used to run network and timers. A supervisor contains a single task listening for connections from sites. A site contain a task for each supervisor that it connects to.

See the async documentation for more information about working with task and concurrency.

Archive and Logging

Sites and supervisor can log message and events, and will store them in an archive.

RSMP::Archive stores messages. RSMP::Logger filters and formats messages according to log settings.

By default, sites and supervisor will create a new archive when initialized, but you can pass in an exsiting archive, which is useful in case you want several sites/supervisors to use the same archive:

require 'rsmp'

# create common archive and logger
logger = RSMP::Logger.new('timestamp'=>false,'author'=>true)
archive = RSMP::Archive.new

# run supervisor and site for 0.1 second, then stop them
Async do |task|
  RSMP::Supervisor.new(archive:archive,logger:logger).start
  RSMP::Site.new(archive:archive,logger:logger).start
  task.sleep 0.1
  task.stop
end

# show archiuve content
logger.dump archive, force:true

This will output messages form both the site and the supervisor, ordered chronologically:

RN+SU0001                              Starting supervisor RN+SU0001 on port 12111
RN+SI0001                              Starting site RN+SI0001
RN+SI0001                              Connecting to supervisor at 127.0.0.1:12111
RN+SI0001                   <--  f8c7  Sent Version
RN+SU0001                              Site connected from 127.0.0.1:53500
RN+SU0001     RN+SI0001     -->  f8c7  Received Version message for sites [RN+SI0001] using RSMP 3.1.4
...

JSON Schema validation

All messages sent and received will be validated against the core RSMP JSON Schema.

Command-line tool

Tools for easily running RSMP supervisors and sites. The binary is called rsmp.

The supervisor command will start an RSMP supervisor, which sites can connect to:

% rsmp supervisor
2019-11-11 12:21:55 UTC                            Starting supervisor RN+SU0001 on port 12111
2019-11-11 12:22:00 UTC                            Site connected from 127.0.0.1:50098
2019-11-11 12:22:00 UTC  RN+SI0001      -->  792f  Received Version message for sites [RN+SI0001] using RSMP 3.1.4
2019-11-11 12:22:00 UTC  RN+SI0001      <--  e70e  Sent Version
2019-11-11 12:22:00 UTC  RN+SI0001                 Connection to site RN+SI0001 established
2019-11-11 12:22:00 UTC  RN+SI0001                 Adding component C1 to site RN+SI0001
2019-11-11 12:22:00 UTC  RN+SI0001  C1  -->  8280  Received AggregatedStatus status for component C1 []

The site command will start an RSMP site, which will try to connect to one or more supervisor. Here's an example of the site connecting to a Ruby supervisor:

% rsmp site
2019-11-11 12:22:00 UTC                            Starting site RN+SI0001
2019-11-11 12:22:00 UTC                            Connecting to supervisor at 127.0.0.1:12111
2019-11-11 12:22:00 UTC                 <--  792f  Sent Version
2019-11-11 12:22:00 UTC  RN+SU0001      -->  e70e  Received Version message, using RSMP 3.1.4
2019-11-11 12:22:00 UTC  RN+SU0001                 Connection to supervisor established
2019-11-11 12:22:00 UTC  RN+SU0001  C1  <--  8280  Sent AggregatedStatus

Use the the --type switch to select a specific type of site. Messages will be validated against the corresponding SXL JSON schema. Without any type specified, messages will be validated against the core RSMP schema only.

Use the tlc site type to run an emulation of a traffic light controller. This type of site implements enough of functionality to pass all the rsmp_validator tests. You can setup signal group components, in the config file.

CLI help and options.

Use --help <command> to get a list of available options.

Use --config <path> to point to a .yaml config file, controlling things like IP adresses, ports, and log output. Examples of config files can be found the folder config/.

Tests

RSpec

RSpec tests are located in spec/. The tests will start supervisor and sites to test communication, but will do so on port 13111, rather than the usual port 12111, to avoid inferference with other RMSP processes running locally.

Note that these tests are NOT intented for testing external equipment or systems. The tests are for validating the code in this repository. To test external equipment or systems use the rsmp_validator tool.

$ rspec
.........................

Finished in 0.12746 seconds (files took 0.6571 seconds to load)
25 examples, 0 failures

Cucumber

Cucumber is used to test the CLI binaries.

$ cucumber
Feature: Help

  Scenario: Displaying help              # features/help.feature:3
    When I run `rsmp help`               # aruba-0.14.11/lib/aruba/cucumber/command.rb:6
    Then it should pass with "Commands:" # aruba-0.14.11/lib/aruba/cucumber/command.rb:271

Feature: Run site
...

7 scenarios (7 passed)
28 steps (28 passed)
0m7.036s