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

#core_version, #logger, #proxies, #site_settings

Attributes included from Components

#components

Attributes inherited from Node

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

Attributes included from RSMP::Task

#task

Attributes included from Logging

#archive, #logger

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Site

#aggregated_status_changed, #alarm_acknowledged, #alarm_activated_or_deactivated, #alarm_suspended_or_resumed, #build_proxies, #check_core_versions, #check_sxl_version, #connect_to_supervisor, #default_site_settings, #find_supervisor, #handle_site_settings, #log_site_starting, #run, #send_alarm, #site_id, #stop, #sxl_version, #wait_for_supervisor

Methods included from Components

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

Methods inherited from Node

#author, #check_required_settings, #clear_deferred, #defer, #distribute_error, #ignore_errors, #now, #process_deferred

Methods included from RSMP::Task

#initialize_task, #restart, #run, #stop, #stop_task, #task_status, #wait, #wait_for_condition

Methods included from Inspect

#inspect, #inspector

Methods included from Logging

#author, #initialize_logging, #log

Constructor Details

#initialize(options = {}) ⇒ TrafficControllerSite

Returns a new instance of TrafficControllerSite.

Raises:



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]['signal_plans']

  super

  return if main

  raise ConfigurationError, 'TLC must have a main component'
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) ⇒ Boolean

Returns:

  • (Boolean)


143
144
145
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 143

def self.from_rsmp_bool?(str)
  str == 'True'
end

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



147
148
149
150
151
152
153
154
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 147

def self.make_status(value, quality = 'recent')
  case value
  when true, false
    [to_rmsp_bool(value), quality]
  else
    [value, quality]
  end
end

.to_rmsp_bool(bool) ⇒ Object



135
136
137
138
139
140
141
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 135

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

Instance Method Details

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



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 57

def build_component(id:, type:, settings: {})
  case type
  when 'main'
    TrafficController.new node: self,
                          id: id,
                          ntsoid: settings['ntsOId'],
                          xnid: settings['xNId'],
                          startup_sequence: @startup_sequence,
                          signal_plans: @signal_plans,
                          live_output: @site_settings['live_output'],
                          inputs: @site_settings['inputs']
  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



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

def build_plans(signal_plans)
  @signal_plans = {}
  return unless signal_plans

  signal_plans.each_pair do |id, settings|
    states = nil
    cycle_time = settings['cycle_time']
    states = settings['states'] if settings
    dynamic_bands = settings['dynamic_bands'] if settings

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

#change_security_code(level, old_code, new_code) ⇒ Object



130
131
132
133
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 130

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

#do_deferred(key, _item = nil) ⇒ Object



156
157
158
159
160
161
162
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 156

def do_deferred(key, _item = nil)
  case key
  when :restart
    log 'Restarting TLC', level: :info
    restart
  end
end

#get_plan(_group_id, _plan_nr) ⇒ Object



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

def get_plan(_group_id, _plan_nr)
  'NN1BB1'
end

#run_tlc_timer(task) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 89

def run_tlc_timer(task)
  next_time = Time.now.to_f
  loop do
    timer(@clock.now)
  rescue StandardError => e
    distribute_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

#site_type_nameObject



23
24
25
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 23

def site_type_name
  'TLC'
end

#startObject



27
28
29
30
31
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 27

def start
  super
  start_tlc_timer
  main.initiate_startup_sequence
end

#start_tlc_timerObject



79
80
81
82
83
84
85
86
87
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 79

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

  @timer = @task.async do |task|
    task.annotate task_name
    run_tlc_timer task
  end
end

#stop_subtasksObject



33
34
35
36
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 33

def stop_subtasks
  stop_tlc_timer
  super
end

#stop_tlc_timerObject



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

def stop_tlc_timer
  return unless @timer

  @timer.stop
  @timer = nil
end

#timer(now) ⇒ Object



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

def timer(now)
  return unless main

  main.timer now
end

#verify_security_code(level, code) ⇒ Object

Raises:

  • (ArgumentError)


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

def verify_security_code(level, code)
  raise ArgumentError, "Level must be 1-2, got #{level}" unless (1..2).include?(level)
  return unless @security_codes[level] != code

  raise MessageRejected, "Wrong security code for level #{level}"
end