rsmp 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c4431d2545043cf60f1988e1bbb269e5727719fb6dd152c58908c5cd62b153ef
4
- data.tar.gz: 4cb2c6fe95608ae4093876d2630e5a29547fdea1d6f66cdb697855e7a301470a
3
+ metadata.gz: c9a318474a865aca223c43e6bfe2bb2bf55a1e0a4584a7d28c80fc87295bc1f9
4
+ data.tar.gz: 64e892b4fe5b4dbd4aa910542634d82e9120f6c698eb57920c718657be200c1d
5
5
  SHA512:
6
- metadata.gz: 0dcf33801ca97d92861380ed7ee4a4cb59a343eb0744daf448afbe20fa91e2147a8a59359420cd76dd62b1cbe49026b36e284d6534d8cd71538f68ea378e06c3
7
- data.tar.gz: 59e10a8475fa23b0e0d66c257ee5458acb156db1390e88ae464a73b40b63bea3be7372304bd5d331fb0dfaaf19308c731d3aaae158de5d7f16ebe67bb17ddda3
6
+ metadata.gz: 0c9cada8fb00a82666968a656e6896f35942ef480d0ee0e5a442be38b25cc20b89460c6317e13b3087a2c2e7b70432bd8dd69d33a010cca13685f100fc4c0aed
7
+ data.tar.gz: 305d823c21f5de5f32fc143fdc5d7ad0e2c7947940fd4b261e6dd94b222c58657173cacf14c7a4aae59b263eebb1d5d7060d1b0645a99cd414a76cc8d65d55e0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rsmp (0.6.2)
4
+ rsmp (0.7.0)
5
5
  async (~> 1.29.1)
6
6
  async-io (~> 1.32.1)
7
7
  colorize (~> 0.8.1)
@@ -96,7 +96,7 @@ GEM
96
96
  thread_safe
97
97
  rake (13.0.3)
98
98
  regexp_parser (2.1.1)
99
- rsmp_schemer (0.3.1)
99
+ rsmp_schemer (0.3.2)
100
100
  json_schemer (~> 0.2.18)
101
101
  rspec (3.10.0)
102
102
  rspec-core (~> 3.10.0)
data/README.md CHANGED
@@ -125,7 +125,7 @@ This will output messages form both the site and the supervisor, ordered chronol
125
125
  ```console
126
126
  RN+SU0001 Starting supervisor RN+SU0001 on port 12111
127
127
  RN+SI0001 Starting site RN+SI0001
128
- RN+SI0001 Connecting to superviser at 127.0.0.1:12111
128
+ RN+SI0001 Connecting to supervisor at 127.0.0.1:12111
129
129
  RN+SI0001 <-- f8c7 Sent Version
130
130
  RN+SU0001 Site connected from 127.0.0.1:53500
131
131
  RN+SU0001 RN+SI0001 --> f8c7 Received Version message for sites [RN+SI0001] using RSMP 3.1.4
@@ -156,7 +156,7 @@ The ```site``` command will start an RSMP site, which will try to connect to one
156
156
  ```console
157
157
  % rsmp site
158
158
  2019-11-11 12:22:00 UTC Starting site RN+SI0001
159
- 2019-11-11 12:22:00 UTC Connecting to superviser at 127.0.0.1:12111
159
+ 2019-11-11 12:22:00 UTC Connecting to supervisor at 127.0.0.1:12111
160
160
  2019-11-11 12:22:00 UTC <-- 792f Sent Version
161
161
  2019-11-11 12:22:00 UTC RN+SU0001 --> e70e Received Version message, using RSMP 3.1.4
162
162
  2019-11-11 12:22:00 UTC RN+SU0001 Connection to supervisor established
@@ -8,18 +8,10 @@ guest:
8
8
  watchdog: 0.2
9
9
  acknowledgement: 0.2
10
10
  log:
11
- active: true
12
- color: true
13
- timestamp: true
14
- component: true
15
- ip: false
16
11
  port: true
17
12
  site_id: 9
18
- component_id: 3
19
- text: true
13
+ component: 2
20
14
  direction: false
21
15
  level: false
22
16
  debug: true
23
17
  json: true
24
- acknowledgements: false
25
- watchdogs: false
data/config/tlc.yaml CHANGED
@@ -10,15 +10,27 @@ components:
10
10
  cycle_time: 6
11
11
  signal_group:
12
12
  A1:
13
- plan: '11NBBB'
14
13
  A2:
15
- plan: '1NBBBB'
16
14
  B1:
17
- plan: 'BBB11N'
18
15
  B2:
19
- plan: 'BBB1NB'
20
16
  detector_logic:
21
17
  DL1:
18
+ signal_plans:
19
+ 1:
20
+ dynamic_bands:
21
+ 1: 0
22
+ 2: 5
23
+ states:
24
+ A1: '11NBBB'
25
+ A2: '1NBBBB'
26
+ B1: 'BBB11N'
27
+ B2: 'BBB1NB'
28
+ 2:
29
+ states:
30
+ A1: '111NBB'
31
+ A2: '11NBBB'
32
+ B1: 'BBB11N'
33
+ B2: 'BBB1NB'
22
34
  intervals:
23
35
  timer: 0.1
24
36
  watchdog: 0.1
@@ -30,17 +42,9 @@ security_codes:
30
42
  1: '1111'
31
43
  2: '2222'
32
44
  log:
33
- active: true
34
- color: true
35
- timestamp: true
36
- component_id: true
37
45
  ip: false
38
46
  site_id: 9
39
- component: 3
47
+ component: 2
40
48
  level: false
41
49
  debug: true
42
- text: true
43
- direction: false
44
50
  json: true
45
- acknowledgements: false
46
- watchdogs: false
data/lib/rsmp/archive.rb CHANGED
@@ -21,7 +21,7 @@ module RSMP
21
21
  def self.prepare_item item
22
22
  raise ArgumentError unless item.is_a? Hash
23
23
 
24
- cleaned = item.select { |k,v| [:author,:level,:ip,:port,:site_id,:component,:str,:message,:exception].include? k }
24
+ cleaned = item.select { |k,v| [:author,:level,:ip,:port,:site_id,:component,:text,:message,:exception].include? k }
25
25
  cleaned[:timestamp] = Clock.now
26
26
  if item[:message]
27
27
  cleaned[:direction] = item[:message].direction
data/lib/rsmp/cli.rb CHANGED
@@ -51,7 +51,7 @@ module RSMP
51
51
  if options[:type]
52
52
  case options[:type]
53
53
  when 'tlc'
54
- site_class = RSMP::Tlc
54
+ site_class = RSMP::TLC::TrafficControllerSite
55
55
  else
56
56
  site_class = RSMP::Site
57
57
  end
@@ -65,7 +65,8 @@ module RSMP
65
65
  end
66
66
 
67
67
  def log str, options
68
- @node.log str, options
68
+ default = { component: c_id}
69
+ @node.log str, default.merge(options)
69
70
  end
70
71
 
71
72
  def handle_command command_code, arg
@@ -78,16 +79,17 @@ module RSMP
78
79
 
79
80
  def handle_alarm message
80
81
  code = message.attribute('aCId')
81
- alarm = @alarms[code]
82
- if alarm
83
- if alarm.differ? message
84
- @alarms[code] = alarm
85
- else
86
- raise RepeatedAlarmError.new("no changes from previous alarm #{alarm.m_id_short}")
82
+ previous = @alarms[code]
83
+ if previous
84
+ unless message.differ?(previous)
85
+ raise RepeatedAlarmError.new("no changes from previous alarm #{previous.m_id_short}")
86
+ end
87
+ if Time.parse(message.attribute('aTs')) < Time.parse(previous.attribute('aTs'))
88
+ raise RepeatedAlarmError.new("timestamp is earlier than previous alarm #{previous.m_id_short}")
87
89
  end
88
- else
89
- @alarms[code] = message
90
90
  end
91
+ ensure
92
+ @alarms[code] = message
91
93
  end
92
94
 
93
95
  # Handle an incoming status respone, by storing the values
@@ -118,7 +120,6 @@ module RSMP
118
120
  @statuses[sCI][n] ||= {}
119
121
  @statuses[sCI][n][:initial] = true
120
122
  end
121
- #pp @subscribes
122
123
  end
123
124
 
124
125
  # Our proxy unsubscribed to status updates.
@@ -52,7 +52,8 @@ module RSMP
52
52
  component = inferred.new node: self, id: component_id
53
53
  @components[ component_id] = component
54
54
  class_name = component.class.name.split('::').last
55
- log "Inferred #{class_name} component #{component_id}", level: :info
55
+ class_name << " compoent" unless class_name == 'Component'
56
+ log "Inferred #{class_name} #{component_id}", level: :debug
56
57
  component
57
58
  else
58
59
  raise UnknownComponent.new("Component #{component_id} not found") unless component
data/lib/rsmp/error.rb CHANGED
@@ -64,4 +64,7 @@ module RSMP
64
64
 
65
65
  class RepeatedStatusError < Error
66
66
  end
67
+
68
+ class TimestampError < Error
69
+ end
67
70
  end
data/lib/rsmp/logger.rb CHANGED
@@ -5,7 +5,7 @@ module RSMP
5
5
 
6
6
  def initialize settings={}
7
7
  defaults = {
8
- 'active'=>false,
8
+ 'active'=>true,
9
9
  'path'=>nil,
10
10
  'stream'=>nil,
11
11
  'color'=>true,
@@ -13,6 +13,8 @@ module RSMP
13
13
  'statistics'=>false,
14
14
  'hide_ip_and_port' => false,
15
15
  'acknowledgements' => false,
16
+ 'watchdogs' => false,
17
+ 'alarms' => true,
16
18
  'json'=>false,
17
19
  'tabs'=>'-',
18
20
 
@@ -27,7 +29,7 @@ module RSMP
27
29
  'direction'=>false,
28
30
  'level'=>false,
29
31
  'id'=>true,
30
- 'str'=>true,
32
+ 'text'=>true,
31
33
  }
32
34
 
33
35
  default_lengths = {
@@ -38,11 +40,20 @@ module RSMP
38
40
  'port'=>5,
39
41
  'site_id'=>19,
40
42
  'component'=>19,
41
- 'direction'=>4,
43
+ 'direction'=>3,
42
44
  'level'=>7,
43
45
  'id'=>4,
44
46
  }
45
47
 
48
+ @ignorable = {
49
+ 'versions' => ['Version'],
50
+ 'statuses' => ['StatusRequest','StatusSubscribe','StatusUnsubscribe','StatusResponse','StatusUpdate'],
51
+ 'commands' => ['CommandRequest','CommandResponse'],
52
+ 'watchdogs' => 'Watchdog',
53
+ 'alarms' => ['Alarm','AlarmRequest'],
54
+ 'aggregated_status' => ['AggregatedStatus','AggregatedStatusRequest']
55
+ }
56
+
46
57
  if settings
47
58
  @settings = defaults.merge settings
48
59
  else
@@ -95,10 +106,15 @@ module RSMP
95
106
  if item[:message]
96
107
  type = item[:message].type
97
108
  ack = type == "MessageAck" || type == "MessageNotAck"
98
- if @settings["watchdogs"] == false
99
- return false if type == "Watchdog"
100
- if ack
101
- return false if item[:message].original && item[:message].original.type == "Watchdog"
109
+ @ignorable.each_pair do |key,types|
110
+ next unless types
111
+ ignore = [types].flatten
112
+ if @settings[key] == false
113
+ #p [type,ignore_type, [ignore_type].flatten.include?(type)]
114
+ return false if ignore.include?(type)
115
+ if ack
116
+ return false if item[:message].original && ignore.include?(item[:message].original.type)
117
+ end
102
118
  end
103
119
  end
104
120
  return false if ack && @settings["acknowledgements"] == false &&
@@ -190,14 +206,14 @@ module RSMP
190
206
  build_part( parts, item, :port )
191
207
  build_part( parts, item, :site_id )
192
208
  build_part( parts, item, :component )
193
- build_part( parts, item, :direction ) { |part| {in:"-->",out:"<--"}[part] }
209
+ build_part( parts, item, :direction ) { |part| {in:"In",out:"Out"}[part] }
194
210
  build_part( parts, item, :level ) { |part| part.capitalize }
195
211
  build_part( parts, item, :id ) { Logger.shorten_message_id(item[:message].m_id,4) if item[:message] }
196
- build_part( parts, item, :str )
212
+ build_part( parts, item, :text )
197
213
  build_part( parts, item, :json ) { item[:message].json if item[:message] }
198
214
  build_part( parts, item, :exception ) { |e| [e.class,e.backtrace].flatten.join("\n") }
199
215
 
200
- parts.join(' ').chomp(@settings['tabs']).rstrip
216
+ parts.join(' ').chomp(@settings['tabs'].to_s).rstrip
201
217
  end
202
218
  end
203
219
  end
data/lib/rsmp/logging.rb CHANGED
@@ -15,7 +15,7 @@ module RSMP
15
15
  end
16
16
 
17
17
  def log str, options={}
18
- default = { str:str, level: :log, author: author, ip: @ip, port: @port }
18
+ default = { text:str, level: :log, author: author, ip: @ip, port: @port }
19
19
  prepared = RSMP::Archive.prepare_item default.merge(options)
20
20
  @archive.add prepared
21
21
  @logger.log prepared
data/lib/rsmp/message.rb CHANGED
@@ -126,7 +126,7 @@ module RSMP
126
126
  end
127
127
 
128
128
  def initialize attributes = {}
129
- @timestamp = Time.now # this timestamp is for internal use, and does not the clock
129
+ @timestamp = Time.now # this timestamp is for internal use, and does not use the clock
130
130
  # in the node, which can be set by an rsmp supervisor
131
131
 
132
132
  @attributes = { "mType"=> "rSMsg" }.merge attributes
data/lib/rsmp/proxy.rb CHANGED
@@ -348,20 +348,23 @@ module RSMP
348
348
  # cannot send NotAcknowledged for a malformed message since we can't read it, just ignore it
349
349
  nil
350
350
  rescue SchemaError, RSMP::Schemer::Error => e
351
- str = "Received invalid #{message.type}, schema errors: #{e.message}"
351
+ reason = "schema errors: #{e.message}"
352
+ str = "Received invalid #{message.type}, #{reason}"
352
353
  log str, message: message, level: :warning
353
354
  notify_error e.exception(str), message: message
354
- dont_acknowledge message, str
355
+ dont_acknowledge message, str, reason
355
356
  message
356
357
  rescue InvalidMessage => e
357
- str = "Received", "invalid #{message.type}, #{e.message}"
358
+ reason = "#{e.message}"
359
+ str = "Received invalid #{message.type},"
358
360
  notify_error e.exception("#{str} #{message.json}"), message: message
359
- dont_acknowledge message, str
361
+ dont_acknowledge message, str, reason
360
362
  message
361
363
  rescue FatalError => e
364
+ reason = e.message
362
365
  str = "Rejected #{message.type},"
363
366
  notify_error e.exception(str), message: message
364
- dont_acknowledge message, str, "#{e.message}"
367
+ dont_acknowledge message, str, reason
365
368
  stop
366
369
  message
367
370
  ensure
@@ -23,15 +23,22 @@ module RSMP
23
23
  end
24
24
 
25
25
  def start
26
- log "Connecting to superviser at #{@ip}:#{@port}", level: :info
26
+ log "Connecting to supervisor at #{@ip}:#{@port}", level: :info
27
27
  super
28
28
  connect
29
29
  @logger.unmute @ip, @port
30
- log "Connected to superviser at #{@ip}:#{@port}", level: :info
30
+ log "Connected to supervisor at #{@ip}:#{@port}", level: :info
31
31
  start_reader
32
32
  send_version @site_settings['site_id'], @site_settings["rsmp_versions"]
33
- rescue Errno::ECONNREFUSED
34
- log "No connection to supervisor at #{@ip}:#{@port}", level: :error
33
+ rescue SystemCallError => e
34
+ log "Could not connect to supervisor at #{@ip}:#{@port}: Errno #{e.errno} #{e}", level: :error
35
+ retry_notice
36
+ rescue StandardError => e
37
+ log "Error while connecting to supervisor at #{@ip}:#{@port}: #{e}", level: :error
38
+ retry_notice
39
+ end
40
+
41
+ def retry_notice
35
42
  unless @site.site_settings['intervals']['reconnect'] == :no
36
43
  log "Will try to reconnect again every #{@site.site_settings['intervals']['reconnect']} seconds..", level: :info
37
44
  @logger.mute @ip, @port
@@ -0,0 +1,101 @@
1
+ module RSMP
2
+ module TLC
3
+ class DetectorLogic < Component
4
+ attr_reader :forced, :value
5
+
6
+ def initialize node:, id:
7
+ super node: node, id: id, grouped: false
8
+ @forced = 0
9
+ @value = 0
10
+ end
11
+
12
+ def get_status code, name=nil
13
+ case code
14
+ when 'S0201', 'S0202', 'S0203', 'S0204'
15
+ return send("handle_#{code.downcase}", code, name)
16
+ else
17
+ raise InvalidMessage.new "unknown status code #{code}"
18
+ end
19
+ end
20
+
21
+ def handle_s0201 status_code, status_name=nil
22
+ case status_name
23
+ when 'starttime'
24
+ TrafficControllerSite.make_status @node.clock.to_s
25
+ when 'vehicles'
26
+ TrafficControllerSite.make_status 0
27
+ end
28
+ end
29
+
30
+ def handle_s0202 status_code, status_name=nil
31
+ case status_name
32
+ when 'starttime'
33
+ TrafficControllerSite.make_status @node.clock.to_s
34
+ when 'speed'
35
+ TrafficControllerSite.make_status 0
36
+ end
37
+ end
38
+
39
+ def handle_s0203 status_code, status_name=nil
40
+ case status_name
41
+ when 'starttime'
42
+ TrafficControllerSite.make_status @node.clock.to_s
43
+ when 'occupancy'
44
+ TrafficControllerSite.make_status 0
45
+ end
46
+ end
47
+
48
+ def handle_s0204 status_code, status_name=nil
49
+ case status_name
50
+ when 'starttime'
51
+ TrafficControllerSite.make_status @node.clock.to_s
52
+ when 'P'
53
+ TrafficControllerSite.make_status 0
54
+ when 'PS'
55
+ TrafficControllerSite.make_status 0
56
+ when 'L'
57
+ TrafficControllerSite.make_status 0
58
+ when 'LS'
59
+ TrafficControllerSite.make_status 0
60
+ when 'B'
61
+ TrafficControllerSite.make_status 0
62
+ when 'SP'
63
+ TrafficControllerSite.make_status 0
64
+ when 'MC'
65
+ TrafficControllerSite.make_status 0
66
+ when 'C'
67
+ TrafficControllerSite.make_status 0
68
+ when 'F'
69
+ TrafficControllerSite.make_status 0
70
+ end
71
+ end
72
+
73
+ def handle_command command_code, arg
74
+ case command_code
75
+ when 'M0008'
76
+ handle_m0008 arg
77
+ else
78
+ raise UnknownCommand.new "Unknown command #{command_code}"
79
+ end
80
+ end
81
+
82
+ def handle_m0008 arg
83
+ @node.verify_security_code 2, arg['securityCode']
84
+ status = arg['status']=='True'
85
+ mode = arg['mode']=='True'
86
+ force_detector_logic status, mode
87
+ arg
88
+ end
89
+
90
+ def force_detector_logic forced, value
91
+ @forced = forced
92
+ @value = value
93
+ if @forced
94
+ log "Forcing to #{value}", level: :info
95
+ else
96
+ log "Releasing", level: :info
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,85 @@
1
+ module RSMP
2
+ module TLC
3
+ class SignalGroup < Component
4
+ attr_reader :plan, :state
5
+
6
+ # plan is a string, with each character representing a signal phase at a particular second in the cycle
7
+ def initialize node:, id:
8
+ super node: node, id: id, grouped: false
9
+ move 0
10
+ end
11
+
12
+ def get_state pos
13
+ default = 'a' # phase a means disabled/dark
14
+ plan = node.main.current_plan
15
+ return default unless plan
16
+ return default unless plan.states
17
+ states = plan.states[c_id]
18
+ return default unless states
19
+ state =states[pos]
20
+ return default unless state =~ /[a-hA-G0-9N-P]/ # valid signal group states
21
+ state
22
+ end
23
+
24
+ def move pos
25
+ @state = get_state pos
26
+ end
27
+
28
+ def handle_command command_code, arg
29
+ case command_code
30
+ when 'M0010', 'M0011'
31
+ return send("handle_#{command_code.downcase}", arg)
32
+ else
33
+ raise UnknownCommand.new "Unknown command #{command_code}"
34
+ end
35
+ end
36
+
37
+ # Start of signal group. Orders a signal group to green
38
+ def handle_m0010 arg
39
+ @node.verify_security_code 2, arg['securityCode']
40
+ if TrafficControllerSite.from_rsmp_bool arg['status']
41
+ log "Start signal group #{c_id}, go to green", level: :info
42
+ end
43
+ end
44
+
45
+ # Stop of signal group. Orders a signal group to red
46
+ def handle_m0011 arg
47
+ @node.verify_security_code 2, arg['securityCode']
48
+ if TrafficControllerSite.from_rsmp_bool arg['status']
49
+ log "Stop signal group #{c_id}, go to red", level: :info
50
+ end
51
+ end
52
+
53
+ def get_status code, name=nil
54
+ case code
55
+ when 'S0025'
56
+ return send("handle_#{code.downcase}", code, name)
57
+ else
58
+ raise InvalidMessage.new "unknown status code #{code}"
59
+ end
60
+ end
61
+
62
+ def handle_s0025 status_code, status_name=nil
63
+ now = @node.clock.to_s
64
+ case status_name
65
+ when 'minToGEstimate'
66
+ TrafficControllerSite.make_status now
67
+ when 'maxToGEstimate'
68
+ TrafficControllerSite.make_status now
69
+ when 'likelyToGEstimate'
70
+ TrafficControllerSite.make_status now
71
+ when 'ToGConfidence'
72
+ TrafficControllerSite.make_status 0
73
+ when 'minToREstimate'
74
+ TrafficControllerSite.make_status now
75
+ when 'maxToREstimate'
76
+ TrafficControllerSite.make_status now
77
+ when 'likelyToREstimate'
78
+ TrafficControllerSite.make_status now
79
+ when 'ToRConfidence'
80
+ TrafficControllerSite.make_status 0
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,29 @@
1
+ module RSMP
2
+ module TLC
3
+ # A Traffic Light Controller Signal Plan.
4
+ # A signal plan is a description of how all signal groups should change
5
+ # state over time.
6
+ class SignalPlan
7
+ attr_reader :nr, :states, :dynamic_bands
8
+ def initialize nr:, states:, dynamic_bands:
9
+ @nr = nr
10
+ @states = states
11
+ @dynamic_bands = dynamic_bands || {}
12
+ end
13
+
14
+ def dynamic_bands_string
15
+ str = @dynamic_bands.map { |band,value| "#{nr}-#{band}-#{value}" }.join(',')
16
+ return nil if str == ''
17
+ str
18
+ end
19
+
20
+ def set_band band, value
21
+ @dynamic_bands[ band.to_i ] = value.to_i
22
+ end
23
+
24
+ def get_band band
25
+ @dynamic_bands[ band.to_i ]
26
+ end
27
+ end
28
+ end
29
+ end