Class: RSMP::TLC::TrafficControllerSite

Inherits:
Site
  • Object
show all
Defined in:
lib/rsmp/tlc/traffic_controller_site.rb

Overview

Simulates a Traffic Light Controller Site

Instance Attribute Summary collapse

Attributes inherited from Site

#logger, #proxies, #rsmp_versions, #site_settings

Attributes included from Components

#components

Attributes inherited from Node

#archive, #clock, #collector, #deferred, #error_queue, #logger, #task

Attributes included from Logging

#archive, #logger

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Site

#aggregated_status_changed, #alarm, #build_proxy, #check_sxl_version, #connect_to_supervisor, #find_supervisor, #handle_site_settings, #reconnect, #run_site_proxy, #site_id, #starting, #stop, #wait_for_supervisor

Methods included from Components

#add_component, #aggregated_status_changed, #check_main_component, #find_component, #infer_component_type, #initialize_components, #setup_components

Methods inherited from Node

#author, #check_required_settings, #clear_deferred, #defer, #do_start, #exiting, #idle, #ignore_errors, #notify_error, #process_deferred, #restart, #start, #stop

Methods included from Inspect

#inspect, #inspector

Methods included from Wait

#wait_for

Methods included from Logging

#author, #initialize_logging, #log

Constructor Details

#initialize(options = {}) ⇒ TrafficControllerSite

Returns a new instance of TrafficControllerSite.



7
8
9
10
11
12
13
14
15
16
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 7

def initialize options={}
  @sxl = 'traffic_light_controller'
  @security_codes = options[:site_settings]['security_codes']
  @interval = options[:site_settings].dig('intervals','timer') || 1
  build_plans options[:site_settings].dig('signal_plans')
  super options
  unless @main
    raise ConfigurationError.new "TLC must have a main component"
  end
end

Instance Attribute Details

#mainObject

Returns the value of attribute main.



5
6
7
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 5

def main
  @main
end

#signal_plansObject

Returns the value of attribute signal_plans.



5
6
7
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 5

def signal_plans
  @signal_plans
end

Class Method Details

.from_rsmp_bool(str) ⇒ Object



119
120
121
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 119

def self.from_rsmp_bool str
  str == 'True'
end

.make_status(value, q = 'recent') ⇒ Object



123
124
125
126
127
128
129
130
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 123

def self.make_status value, q='recent'
  case value
  when true, false
    return to_rmsp_bool(value), q
  else
    return value, q
  end
end

.to_rmsp_bool(bool) ⇒ Object



111
112
113
114
115
116
117
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 111

def self.to_rmsp_bool bool
  if bool
    'True'
  else
    'False'
  end
end

Instance Method Details

#build_component(id:, type:, settings: {}) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 35

def build_component id:, type:, settings:{}
  component = case type
  when 'main'
    @main = TrafficController.new node: self, id: id,
      cycle_time: settings['cycle_time'],
      signal_plans: @signal_plans
  when 'signal_group'
    group = SignalGroup.new node: self, id: id
    @main.add_signal_group group
    group
  when 'detector_logic'
    logic = DetectorLogic.new node: self, id: id
    @main.add_detector_logic logic
    logic
  end
end

#build_plans(signal_plans) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 18

def build_plans signal_plans
  @signal_plans = {}
  return unless signal_plans
  signal_plans.each_pair do |id,settings|
    states = nil
    bands = nil
    states = settings['states'] if settings
    dynamic_bands = settings['dynamic_bands'] if settings

    @signal_plans[id.to_i] = SignalPlan.new(nr: id.to_i, states:states,dynamic_bands:dynamic_bands)
  end
end

#change_security_code(level, old_code, new_code) ⇒ Object



106
107
108
109
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 106

def change_security_code level, old_code, new_code
  verify_security_code level, old_code
  @security_codes[level] = new_code
end

#do_deferred(item) ⇒ Object



132
133
134
135
136
137
138
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 132

def do_deferred item
  case item
  when :restart
    log "Restarting TLC", level: :info
    restart
  end
end

#get_plan(group_id, plan_nr) ⇒ Object



31
32
33
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 31

def get_plan group_id, plan_nr
  'NN1BB1'
end

#start_actionObject



52
53
54
55
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 52

def start_action
  super
  start_timer
end

#start_timerObject



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 57

def start_timer
  task_name = "tlc timer"
  log "Starting #{task_name} with interval #{@interval} seconds", level: :debug

  @timer = @task.async do |task|
    task.annotate task_name
    next_time = Time.now.to_f
    loop do
      begin
        timer(@clock.now)
      rescue EOFError => e
        log "Connection closed: #{e}", level: :warning
      rescue IOError => e
        log "IOError", level: :warning
      rescue Errno::ECONNRESET
        log "Connection reset by peer", level: :warning
      rescue Errno::EPIPE => e
        log "Broken pipe", level: :warning
      rescue StandardError => e
        notify_error e, level: :internal
      ensure
        # adjust sleep duration to avoid drift. so wake up always happens on the
        # same fractional second.
        # note that Time.now is not monotonic. If the clock is changed,
        # either manaully or via NTP, the sleep interval might jump.
        # an alternative is to use ::Process.clock_gettime(::Process::CLOCK_MONOTONIC),
        # to get the current time. this ensures a constant interval, but
        # if the clock is changed, the wake up would then happen on a different
        # fractional second
        next_time += @interval
        duration = next_time - Time.now.to_f
        task.sleep duration
      end
    end
  end
end

#timer(now) ⇒ Object



94
95
96
97
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 94

def timer now
  return unless @main
  @main.timer now
end

#verify_security_code(level, code) ⇒ Object

Raises:

  • (ArgumentError)


99
100
101
102
103
104
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 99

def verify_security_code level, code
  raise ArgumentError.new("Level must be 1-2, got #{level}") unless (1..2).include?(level)
  if @security_codes[level] != code
    raise MessageRejected.new("Wrong security code for level #{level}")
  end
end