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
17
18
19
20
21
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 7

def initialize options={}
  # setup options before calling super initializer,
  # since build of components depend on options
  @sxl = 'traffic_light_controller'
  @security_codes = options[:site_settings]['security_codes']
  @interval = options[:site_settings].dig('intervals','timer') || 1
  @startup_sequence = options[:site_settings]['startup_sequence'] || 'efg'
  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



127
128
129
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 127

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

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



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

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



119
120
121
122
123
124
125
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 119

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

Instance Method Details

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



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 40

def build_component id:, type:, settings:{}
  component = case type
  when 'main'
    @main = TrafficController.new node: self, id: id,
      cycle_time: settings['cycle_time'],
      startup_sequence: @startup_sequence,
      signal_plans: @signal_plans,
      live_output: @site_settings['live_output']
  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



23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 23

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



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

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



140
141
142
143
144
145
146
147
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 140

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

#get_plan(group_id, plan_nr) ⇒ Object



36
37
38
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 36

def get_plan group_id, plan_nr
  'NN1BB1'
end

#start_actionObject



59
60
61
62
63
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 59

def start_action
  super
  start_timer
  @main.initiate_startup_sequence
end

#start_timerObject



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
93
94
95
96
97
98
99
100
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 65

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



102
103
104
105
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 102

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

#verify_security_code(level, code) ⇒ Object

Raises:

  • (ArgumentError)


107
108
109
110
111
112
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 107

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