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 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_state_to_message, #alarm_suspended_or_resumed, #build_proxies, #check_sxl_version, #connect_to_supervisor, #find_supervisor, #handle_site_settings, #run, #site_id, #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, #ignore_errors, #notify_error, #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.



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 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



136
137
138
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 136

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

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



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

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



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

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

Instance Method Details

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



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 52

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'],
      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



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

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



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

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



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

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

#get_plan(group_id, plan_nr) ⇒ Object



48
49
50
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 48

def get_plan group_id, plan_nr
  'NN1BB1'
end

#run_tlc_timer(task) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 82

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

#startObject



24
25
26
27
28
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 24

def start
  super
  start_tlc_timer
  @main.initiate_startup_sequence
end

#start_tlc_timerObject



72
73
74
75
76
77
78
79
80
# File 'lib/rsmp/tlc/traffic_controller_site.rb', line 72

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



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

def stop_subtasks
  stop_tlc_timer
  super
end

#stop_tlc_timerObject



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

def stop_tlc_timer
  return unless @timer
  @timer.stop
  @timer = nil
end

#timer(now) ⇒ Object



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

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

#verify_security_code(level, code) ⇒ Object

Raises:

  • (ArgumentError)


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

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